@wilnertech/halopsa-mcp-server 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/.env.example +18 -18
  2. package/LICENSE +21 -21
  3. package/README.md +252 -205
  4. package/dist/api/client.d.ts +7 -2
  5. package/dist/api/client.d.ts.map +1 -1
  6. package/dist/api/client.js +35 -8
  7. package/dist/api/client.js.map +1 -1
  8. package/dist/cache/memory-cache.d.ts +7 -0
  9. package/dist/cache/memory-cache.d.ts.map +1 -1
  10. package/dist/cache/memory-cache.js +7 -0
  11. package/dist/cache/memory-cache.js.map +1 -1
  12. package/dist/cache/prewarm.d.ts +3 -1
  13. package/dist/cache/prewarm.d.ts.map +1 -1
  14. package/dist/cache/prewarm.js +17 -1
  15. package/dist/cache/prewarm.js.map +1 -1
  16. package/dist/schemas/common.d.ts +30 -99
  17. package/dist/schemas/common.d.ts.map +1 -1
  18. package/dist/schemas/common.js +2 -2
  19. package/dist/schemas/common.js.map +1 -1
  20. package/dist/tools/assets.d.ts +69 -297
  21. package/dist/tools/assets.d.ts.map +1 -1
  22. package/dist/tools/batch-operations.d.ts +49 -81
  23. package/dist/tools/batch-operations.d.ts.map +1 -1
  24. package/dist/tools/batch-operations.js +62 -0
  25. package/dist/tools/batch-operations.js.map +1 -1
  26. package/dist/tools/clients.d.ts +24 -92
  27. package/dist/tools/clients.d.ts.map +1 -1
  28. package/dist/tools/reference-data.d.ts +44 -72
  29. package/dist/tools/reference-data.d.ts.map +1 -1
  30. package/dist/tools/reference-data.js +27 -0
  31. package/dist/tools/reference-data.js.map +1 -1
  32. package/dist/tools/registrations.d.ts +7 -1
  33. package/dist/tools/registrations.d.ts.map +1 -1
  34. package/dist/tools/registrations.js +43 -7
  35. package/dist/tools/registrations.js.map +1 -1
  36. package/dist/tools/registry.d.ts +12 -5
  37. package/dist/tools/registry.d.ts.map +1 -1
  38. package/dist/tools/registry.js +26 -2
  39. package/dist/tools/registry.js.map +1 -1
  40. package/dist/tools/sites.d.ts +19 -111
  41. package/dist/tools/sites.d.ts.map +1 -1
  42. package/dist/tools/ticket-actions.d.ts +41 -0
  43. package/dist/tools/ticket-actions.d.ts.map +1 -0
  44. package/dist/tools/ticket-actions.js +222 -0
  45. package/dist/tools/ticket-actions.js.map +1 -0
  46. package/dist/tools/ticket-custom-fields.d.ts +33 -0
  47. package/dist/tools/ticket-custom-fields.d.ts.map +1 -0
  48. package/dist/tools/ticket-custom-fields.js +155 -0
  49. package/dist/tools/ticket-custom-fields.js.map +1 -0
  50. package/dist/tools/ticket-reference-data.d.ts +88 -0
  51. package/dist/tools/ticket-reference-data.d.ts.map +1 -0
  52. package/dist/tools/ticket-reference-data.js +185 -0
  53. package/dist/tools/ticket-reference-data.js.map +1 -0
  54. package/dist/tools/tickets.d.ts +168 -0
  55. package/dist/tools/tickets.d.ts.map +1 -0
  56. package/dist/tools/tickets.js +572 -0
  57. package/dist/tools/tickets.js.map +1 -0
  58. package/dist/tools/users.d.ts +37 -193
  59. package/dist/tools/users.d.ts.map +1 -1
  60. package/dist/types/tickets.d.ts +193 -0
  61. package/dist/types/tickets.d.ts.map +1 -0
  62. package/dist/types/tickets.js +14 -0
  63. package/dist/types/tickets.js.map +1 -0
  64. package/dist/utils/formatter.d.ts +15 -1
  65. package/dist/utils/formatter.d.ts.map +1 -1
  66. package/dist/utils/formatter.js +52 -6
  67. package/dist/utils/formatter.js.map +1 -1
  68. package/package.json +68 -61
  69. package/dist/utils/zod-to-schema.d.ts +0 -29
  70. package/dist/utils/zod-to-schema.d.ts.map +0 -1
  71. package/dist/utils/zod-to-schema.js +0 -182
  72. package/dist/utils/zod-to-schema.js.map +0 -1
package/.env.example CHANGED
@@ -1,18 +1,18 @@
1
- # HaloPSA MCP Server Environment Configuration
2
- # Copy this file to .env and fill in your actual values
3
- # NEVER commit the .env file to git
4
-
5
- # HaloPSA OAuth2 Client ID (required)
6
- # Get from: HaloPSA > Configuration > Integrations > API Applications
7
- HALOPSA_CLIENT_ID=your-client-id-here
8
-
9
- # HaloPSA OAuth2 Client Secret (required)
10
- HALOPSA_CLIENT_SECRET=your-client-secret-here
11
-
12
- # HaloPSA Base URL (required)
13
- # Your HaloPSA instance URL (e.g., https://your-instance.halopsa.com)
14
- HALOPSA_BASE_URL=https://your-instance.halopsa.com
15
-
16
- # Test Client ID (required for smoke tests only)
17
- # A client ID to scope test resource creation/cleanup
18
- # HALOPSA_TEST_CLIENT_ID=123
1
+ # HaloPSA MCP Server Environment Configuration
2
+ # Copy this file to .env and fill in your actual values
3
+ # NEVER commit the .env file to git
4
+
5
+ # HaloPSA OAuth2 Client ID (required)
6
+ # Get from: HaloPSA > Configuration > Integrations > API Applications
7
+ HALOPSA_CLIENT_ID=your-client-id-here
8
+
9
+ # HaloPSA OAuth2 Client Secret (required)
10
+ HALOPSA_CLIENT_SECRET=your-client-secret-here
11
+
12
+ # HaloPSA Base URL (required)
13
+ # Your HaloPSA instance URL (e.g., https://your-instance.halopsa.com)
14
+ HALOPSA_BASE_URL=https://your-instance.halopsa.com
15
+
16
+ # Test Client ID (required for smoke tests only)
17
+ # A client ID to scope test resource creation/cleanup
18
+ # HALOPSA_TEST_CLIENT_ID=123
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 halopsa-mcp-server contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 halopsa-mcp-server contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,205 +1,252 @@
1
- # HaloPSA MCP Server
2
-
3
- Model Context Protocol server for HaloPSA API integration with asset, user, and site management.
4
-
5
- ## Key Features
6
-
7
- - **31 tools** for managing HaloPSA assets, users, sites, clients, and reference data
8
- - **Deduplication** with confidence scoring to prevent duplicate billing entities
9
- - **OAuth2 Client Credentials** authentication with automatic token refresh
10
- - **Rate limiting** with exponential backoff (60 requests/minute)
11
- - **In-memory caching** with configurable TTLs and cache prewarm on startup
12
- - **Inflight request dedup** prevents concurrent duplicate API calls
13
- - **Token optimization** via format_options (compact/standard/detailed modes)
14
-
15
- ## Important: Billing Context
16
-
17
- HaloPSA asset and user counts are commonly used for billing calculations in MSP environments.
18
-
19
- - **Only ACTIVE assets (status_id=1) count toward billing**
20
- - **Only ACTIVE users (inactive=false) count toward billing**
21
- - **Duplicates = Billing errors** — use the deduplication tools before creating new records
22
-
23
- ## Installation
24
-
25
- ```bash
26
- npm install halopsa-mcp-server
27
- ```
28
-
29
- Or from source:
30
-
31
- ```bash
32
- git clone https://github.com/SuperGaco/halopsa-mcp-server.git
33
- cd halopsa-mcp-server
34
- npm install
35
- npm run build
36
- ```
37
-
38
- ## Configuration
39
-
40
- ### Environment Variables
41
-
42
- | Variable | Required | Description |
43
- |----------|----------|-------------|
44
- | `HALOPSA_CLIENT_ID` | Yes | OAuth2 Client ID |
45
- | `HALOPSA_CLIENT_SECRET` | Yes | OAuth2 Client Secret |
46
- | `HALOPSA_BASE_URL` | Yes | Your HaloPSA instance URL (e.g., `https://your-instance.halopsa.com`) |
47
-
48
- ### HaloPSA API Setup
49
-
50
- 1. In HaloPSA, navigate to **Configuration > Integrations > API Applications**
51
- 2. Create a new application with OAuth2 Client Credentials
52
- 3. Grant appropriate permissions:
53
- - `read:assets` and `read:customers` for read-only access
54
- - `edit:assets` and `edit:customers` for write operations
55
- 4. Note the Client ID and Client Secret
56
-
57
- ### Claude Desktop Configuration
58
-
59
- Add to your Claude Desktop config:
60
-
61
- ```json
62
- {
63
- "mcpServers": {
64
- "halopsa": {
65
- "command": "node",
66
- "args": ["path/to/halopsa-mcp-server/dist/index.js"],
67
- "env": {
68
- "HALOPSA_CLIENT_ID": "your_client_id",
69
- "HALOPSA_CLIENT_SECRET": "your_client_secret",
70
- "HALOPSA_BASE_URL": "https://your-instance.halopsa.com"
71
- }
72
- }
73
- }
74
- }
75
- ```
76
-
77
- ## Tools Reference
78
-
79
- ### Asset Tools (11)
80
-
81
- | Tool | Description |
82
- |------|-------------|
83
- | `list_halopsa_assets` | List assets with filtering, caching (1min TTL) |
84
- | `get_halopsa_asset` | Get single asset with custom fields |
85
- | `search_halopsa_assets_by_serial` | Search by serial number (key_field) |
86
- | `search_halopsa_assets_by_hostname` | Search by hostname (key_field2) |
87
- | `create_halopsa_asset` | Create new asset |
88
- | `update_halopsa_asset` | Update existing asset |
89
- | `delete_halopsa_asset` | Delete asset |
90
- | `list_halopsa_asset_custom_fields` | Inspect custom fields for an asset |
91
- | `get_halopsa_asset_field_schema` | Analyze custom field definitions |
92
- | `find_halopsa_asset_match` | Deduplication with confidence scoring |
93
- | `scan_halopsa_asset_duplicates` | Scan for duplicate assets |
94
-
95
- ### User Tools (7)
96
-
97
- | Tool | Description |
98
- |------|-------------|
99
- | `list_halopsa_users` | List users with filtering, caching (1min TTL) |
100
- | `get_halopsa_user` | Get single user with custom fields |
101
- | `search_halopsa_users_by_email` | Search by email address |
102
- | `create_halopsa_user` | Create new user |
103
- | `update_halopsa_user` | Update existing user |
104
- | `find_halopsa_user_match` | Deduplication with confidence scoring |
105
- | `scan_halopsa_user_duplicates` | Scan for duplicate users |
106
-
107
- ### Site Tools (4)
108
-
109
- | Tool | Description |
110
- |------|-------------|
111
- | `list_halopsa_sites` | List sites with caching (5min TTL) |
112
- | `get_halopsa_site` | Get single site |
113
- | `create_halopsa_site` | Create new site |
114
- | `find_halopsa_site_match` | Deduplication with fuzzy matching |
115
-
116
- ### Client Tools (3)
117
-
118
- | Tool | Description |
119
- |------|-------------|
120
- | `list_halopsa_clients` | List clients with caching (5min TTL) |
121
- | `get_halopsa_client` | Get single client |
122
- | `search_halopsa_clients` | Search clients by name |
123
-
124
- ### Reference Data Tools (3)
125
-
126
- | Tool | Description |
127
- |------|-------------|
128
- | `list_halopsa_asset_types` | List asset types (1hr cache) |
129
- | `list_halopsa_asset_statuses` | List statuses with billing notes |
130
- | `list_halopsa_contact_types` | List contact types (1hr cache) |
131
-
132
- ### Batch Operations Tools (3)
133
-
134
- | Tool | Description |
135
- |------|-------------|
136
- | `get_halopsa_assets_batch` | Get multiple assets (max 50) |
137
- | `get_halopsa_users_batch` | Get multiple users (max 50) |
138
- | `get_halopsa_sites_batch` | Get multiple sites (max 50) |
139
-
140
- ## Deduplication Algorithms
141
-
142
- ### Asset Deduplication
143
-
144
- | Match Type | Confidence | Action |
145
- |------------|------------|--------|
146
- | Exact serial number | 100% | Auto-update |
147
- | Hostname only (serial mismatch) | 85% | Manual review |
148
- | No match | 0% | Create new |
149
-
150
- ### User Deduplication
151
-
152
- | Match Type | Confidence | Action |
153
- |------------|------------|--------|
154
- | Exact email | 100% | Auto-update |
155
- | UPN match (Entra) | 95% | Auto-update |
156
- | Username only | 70% | Manual review |
157
- | No match | 0% | Create new |
158
-
159
- ### Site Deduplication
160
-
161
- | Match Type | Confidence | Action |
162
- |------------|------------|--------|
163
- | Exact name | 100% | Auto-update |
164
- | Fuzzy name + address | 95% | Auto-update |
165
- | Fuzzy name only | 85% | Manual review |
166
- | No match | 0% | Create new |
167
-
168
- ## Token Optimization
169
-
170
- All tools support `format_options` for reducing response size:
171
-
172
- ```json
173
- {
174
- "format_options": {
175
- "format": "compact",
176
- "fields": ["id", "name", "status_id"],
177
- "omit_empty": true
178
- }
179
- }
180
- ```
181
-
182
- **Formats:**
183
- - `compact`: Minimal fields (id, name, status)
184
- - `standard`: Default fields (no pretty-printing)
185
- - `detailed`: All fields with pretty-printing
186
-
187
- ## Rate Limiting
188
-
189
- - **Limit:** 60 requests/minute per client_id
190
- - **Auto-throttling:** 1 second minimum between requests
191
- - **Retry:** Exponential backoff on 429 errors (1s, 2s, 4s)
192
- - **Token refresh:** 5 minutes before expiration
193
-
194
- ## Development
195
-
196
- ```bash
197
- npm run build # Build
198
- npm run dev # Watch mode
199
- npm start # Start server
200
- npm run test:smoke # Run smoke tests (requires live API credentials)
201
- ```
202
-
203
- ## License
204
-
205
- MIT
1
+ # HaloPSA MCP Server
2
+
3
+ Model Context Protocol server for HaloPSA API integration with asset, user, and site management.
4
+
5
+ ## Key Features
6
+
7
+ - **49 tools** for managing HaloPSA assets, users, sites, clients, tickets, actions, and reference data
8
+ - **Deduplication** with confidence scoring to prevent duplicate billing entities
9
+ - **OAuth2 Client Credentials** authentication with automatic token refresh
10
+ - **Rate limiting** with exponential backoff (60 requests/minute)
11
+ - **In-memory caching** with configurable TTLs and cache prewarm on startup
12
+ - **Inflight request dedup** prevents concurrent duplicate API calls
13
+ - **Token optimization** via format_options (compact/standard/detailed modes)
14
+
15
+ ## Important: Billing Context
16
+
17
+ HaloPSA asset and user counts are commonly used for billing calculations in MSP environments.
18
+
19
+ - **Only ACTIVE assets (status_id=1) count toward billing**
20
+ - **Only ACTIVE users (inactive=false) count toward billing**
21
+ - **Duplicates = Billing errors** — use the deduplication tools before creating new records
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install halopsa-mcp-server
27
+ ```
28
+
29
+ Or from source:
30
+
31
+ ```bash
32
+ git clone https://github.com/SuperGaco/halopsa-mcp-server.git
33
+ cd halopsa-mcp-server
34
+ npm install
35
+ npm run build
36
+ ```
37
+
38
+ ## Configuration
39
+
40
+ ### Environment Variables
41
+
42
+ | Variable | Required | Description |
43
+ |----------|----------|-------------|
44
+ | `HALOPSA_CLIENT_ID` | Yes | OAuth2 Client ID |
45
+ | `HALOPSA_CLIENT_SECRET` | Yes | OAuth2 Client Secret |
46
+ | `HALOPSA_BASE_URL` | Yes | Your HaloPSA instance URL (e.g., `https://your-instance.halopsa.com`) |
47
+ | `HALOPSA_TEST_CLIENT_ID` | Smoke tests only | Integer ID of a non-billable throwaway client used to scope test resource creation/cleanup. Required by `test:smoke` / `test:smoke:local`. |
48
+
49
+ ### HaloPSA API Setup
50
+
51
+ 1. In HaloPSA, navigate to **Configuration > Integrations > API Applications**
52
+ 2. Create a new application with OAuth2 Client Credentials
53
+ 3. Grant appropriate permissions:
54
+ - `read:assets` and `read:customers` for read-only access
55
+ - `edit:assets` and `edit:customers` for write operations
56
+ 4. Note the Client ID and Client Secret
57
+
58
+ ### Claude Desktop Configuration
59
+
60
+ Add to your Claude Desktop config:
61
+
62
+ ```json
63
+ {
64
+ "mcpServers": {
65
+ "halopsa": {
66
+ "command": "node",
67
+ "args": ["path/to/halopsa-mcp-server/dist/index.js"],
68
+ "env": {
69
+ "HALOPSA_CLIENT_ID": "your_client_id",
70
+ "HALOPSA_CLIENT_SECRET": "your_client_secret",
71
+ "HALOPSA_BASE_URL": "https://your-instance.halopsa.com"
72
+ }
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
78
+ ## Tools Reference
79
+
80
+ ### Asset Tools (11)
81
+
82
+ | Tool | Description |
83
+ |------|-------------|
84
+ | `list_halopsa_assets` | List assets with filtering, caching (1min TTL) |
85
+ | `get_halopsa_asset` | Get single asset with custom fields |
86
+ | `search_halopsa_assets_by_serial` | Search by serial number (key_field) |
87
+ | `search_halopsa_assets_by_hostname` | Search by hostname (key_field2) |
88
+ | `create_halopsa_asset` | Create new asset |
89
+ | `update_halopsa_asset` | Update existing asset |
90
+ | `delete_halopsa_asset` | Delete asset |
91
+ | `list_halopsa_asset_custom_fields` | Inspect custom fields for an asset |
92
+ | `get_halopsa_asset_field_schema` | Analyze custom field definitions |
93
+ | `find_halopsa_asset_match` | Deduplication with confidence scoring |
94
+ | `scan_halopsa_asset_duplicates` | Scan for duplicate assets |
95
+
96
+ ### User Tools (7)
97
+
98
+ | Tool | Description |
99
+ |------|-------------|
100
+ | `list_halopsa_users` | List users with filtering, caching (1min TTL) |
101
+ | `get_halopsa_user` | Get single user with custom fields |
102
+ | `search_halopsa_users_by_email` | Search by email address |
103
+ | `create_halopsa_user` | Create new user |
104
+ | `update_halopsa_user` | Update existing user |
105
+ | `find_halopsa_user_match` | Deduplication with confidence scoring |
106
+ | `scan_halopsa_user_duplicates` | Scan for duplicate users |
107
+
108
+ ### Site Tools (4)
109
+
110
+ | Tool | Description |
111
+ |------|-------------|
112
+ | `list_halopsa_sites` | List sites with caching (5min TTL) |
113
+ | `get_halopsa_site` | Get single site |
114
+ | `create_halopsa_site` | Create new site |
115
+ | `find_halopsa_site_match` | Deduplication with fuzzy matching |
116
+
117
+ ### Client Tools (3)
118
+
119
+ | Tool | Description |
120
+ |------|-------------|
121
+ | `list_halopsa_clients` | List clients with caching (5min TTL) |
122
+ | `get_halopsa_client` | Get single client |
123
+ | `search_halopsa_clients` | Search clients by name |
124
+
125
+ ### Reference Data Tools (4)
126
+
127
+ | Tool | Description |
128
+ |------|-------------|
129
+ | `list_halopsa_asset_types` | List asset types (1hr cache) |
130
+ | `list_halopsa_asset_statuses` | List statuses with billing notes |
131
+ | `list_halopsa_contact_types` | List contact types (1hr cache) |
132
+ | `list_halopsa_agents` | List agents (technicians/staff) with 1hr cache. Use to discover `agent_id` for ticket assignment. Fields: `id`, `name`, `email`, `isdisabled` (note: agents use `isdisabled`, NOT `inactive`). |
133
+
134
+ ### Ticket Reference Data Tools (5)
135
+
136
+ | Tool | Description |
137
+ |------|-------------|
138
+ | `list_halopsa_tickettypes` | List ticket types (Incident, Service Request, Change, ...) — 1hr cache |
139
+ | `get_halopsa_tickettype` | Get a single ticket type — 1hr cache |
140
+ | `list_halopsa_ticket_statuses` | List ticket statuses; backs `close_halopsa_ticket` dynamic resolution |
141
+ | `list_halopsa_ticket_priorities` | List ticket priorities — 1hr cache |
142
+ | `list_halopsa_ticket_categories` | List ticket categories (used by category_1..category_4) |
143
+
144
+ ### Ticket Tools (8)
145
+
146
+ | Tool | Description |
147
+ |------|-------------|
148
+ | `list_halopsa_tickets` | List with extensive filtering (client/site/user/agent/team/type/status/category/search/date/open_only). Array filters (`tickettype_id`, `agent`, `category_1..4`) accept single int or int[]. Compact response excludes `details_html` and `customfields[]`. |
149
+ | `get_halopsa_ticket` | Single ticket with full details (`includedetails=true`) |
150
+ | `search_halopsa_tickets` | Free-text search wrapper on list |
151
+ | `create_halopsa_ticket` | POST `/Tickets` with array body — required: summary, tickettype_id, client_id. Optional: parent_id, site_id, agent_id, dateoccurred, startdate, source, category_1..4. |
152
+ | `update_halopsa_ticket` | Read-before-write merge (Halo POST is REPLACE not PATCH). Supports tickettype_id, parent_id, dateoccurred, startdate, source in addition to status/agent/site/summary/details. |
153
+ | `close_halopsa_ticket` | Dynamic closed-status lookup (`name === 'Closed' && type === 0`); optional resolution_note posts a final action first |
154
+ | `delete_halopsa_ticket` | **Safety-first delete** with four pre-flight refusals (financial / cascade / historical / authorship). Required: `ticket_id`, `reason` (min 5 chars). Bypass: `confirm_destructive: true` + substring `'OVERRIDE'` in `reason`. Idempotent on 404. |
155
+ | `get_halopsa_tickets_batch` | Parallel batch fetch (max 50 ids) |
156
+
157
+ ### Ticket Action Tools (2)
158
+
159
+ | Tool | Description |
160
+ |------|-------------|
161
+ | `add_halopsa_ticket_action` | POST `/Actions` with array body — note/outcome/email_reply/hiddenfromuser |
162
+ | `list_halopsa_ticket_actions` | List journal entries; compact mode includes 200-char `note_preview` |
163
+
164
+ ### Ticket Custom Field Tools (2)
165
+
166
+ | Tool | Description |
167
+ |------|-------------|
168
+ | `list_halopsa_ticket_custom_fields` | Per-tickettype field definitions via `/FieldInfo?type=1` |
169
+ | `get_halopsa_ticket_field_schema` | Sampling-based fallback when `/FieldInfo` is unavailable |
170
+
171
+ ### Batch Operations Tools (4)
172
+
173
+ | Tool | Description |
174
+ |------|-------------|
175
+ | `get_halopsa_assets_batch` | Get multiple assets (max 50) |
176
+ | `get_halopsa_users_batch` | Get multiple users (max 50) |
177
+ | `get_halopsa_sites_batch` | Get multiple sites (max 50) |
178
+ | `get_halopsa_tickets_batch` | Get multiple tickets (max 50) |
179
+
180
+ ## Deduplication Algorithms
181
+
182
+ ### Asset Deduplication
183
+
184
+ | Match Type | Confidence | Action |
185
+ |------------|------------|--------|
186
+ | Exact serial number | 100% | Auto-update |
187
+ | Hostname only (serial mismatch) | 85% | Manual review |
188
+ | No match | 0% | Create new |
189
+
190
+ ### User Deduplication
191
+
192
+ | Match Type | Confidence | Action |
193
+ |------------|------------|--------|
194
+ | Exact email | 100% | Auto-update |
195
+ | UPN match (Entra) | 95% | Auto-update |
196
+ | Username only | 70% | Manual review |
197
+ | No match | 0% | Create new |
198
+
199
+ ### Site Deduplication
200
+
201
+ | Match Type | Confidence | Action |
202
+ |------------|------------|--------|
203
+ | Exact name | 100% | Auto-update |
204
+ | Fuzzy name + address | 95% | Auto-update |
205
+ | Fuzzy name only | 85% | Manual review |
206
+ | No match | 0% | Create new |
207
+
208
+ ## Token Optimization
209
+
210
+ All tools support `format_options` for reducing response size:
211
+
212
+ ```json
213
+ {
214
+ "format_options": {
215
+ "format": "compact",
216
+ "fields": ["id", "name", "status_id"],
217
+ "omit_empty": true
218
+ }
219
+ }
220
+ ```
221
+
222
+ **Formats:**
223
+ - `compact`: Minimal fields (id, name, status)
224
+ - `standard`: Default fields (no pretty-printing)
225
+ - `detailed`: All fields with pretty-printing
226
+
227
+ ## Rate Limiting
228
+
229
+ - **Limit:** 60 requests/minute per client_id
230
+ - **Auto-throttling:** 1 second minimum between requests
231
+ - **Retry:** Exponential backoff on 429 errors (1s, 2s, 4s)
232
+ - **Token refresh:** 5 minutes before expiration
233
+
234
+ ## Development
235
+
236
+ ```bash
237
+ npm run build # Build (tsc)
238
+ npm run dev # Watch mode (tsc --watch)
239
+ npm run typecheck # Type-check without emit
240
+ npm run lint # ESLint
241
+ npm test # Jest unit tests (similarity + formatter scrubber)
242
+ npm start # Start server (uses external env)
243
+ npm run start:local # Start server loading .env via Node --env-file
244
+ npm run test:smoke # Run smoke tests against live API (CI/CD env injects creds)
245
+ npm run test:smoke:local # Run smoke tests loading .env via Node --env-file
246
+ ```
247
+
248
+ For local development, copy `.env.example` to `.env` and fill in `HALOPSA_CLIENT_ID`, `HALOPSA_CLIENT_SECRET`, `HALOPSA_BASE_URL`, and `HALOPSA_TEST_CLIENT_ID` (the last is required by `test:smoke:local` only).
249
+
250
+ ## License
251
+
252
+ MIT
@@ -25,14 +25,19 @@ export declare class HaloPSAAPIClient {
25
25
  private accessToken;
26
26
  private tokenExpiresAt;
27
27
  private tokenRefreshBuffer;
28
+ private tokenRefreshPromise;
28
29
  private requestCount;
29
30
  private lastRequestTime;
30
31
  private readonly MIN_REQUEST_INTERVAL;
31
32
  private inflight;
32
33
  constructor(config: HaloPSAClientConfig);
33
34
  /**
34
- * Ensure we have a valid access token
35
- * Requests new token if none exists or if close to expiration
35
+ * Ensure we have a valid access token.
36
+ *
37
+ * Single-flight pattern (Phase 8 BLOCK-1): if a refresh is already in progress
38
+ * (tokenRefreshPromise !== null), all concurrent callers await the same promise
39
+ * instead of each issuing a redundant token request. The promise field is cleared
40
+ * in .finally() so subsequent refreshes after token expiry work correctly.
36
41
  */
37
42
  private ensureValidToken;
38
43
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AASD,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAG3B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,kBAAkB,CAAyB;IAGnD,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAQ;IAG7C,OAAO,CAAC,QAAQ,CAA4C;gBAEhD,MAAM,EAAE,mBAAmB;IA+CvC;;;OAGG;YACW,gBAAgB;IAS9B;;OAEG;YACW,YAAY;IA0C1B;;;OAGG;YACW,eAAe;IAiB7B;;OAEG;YACW,WAAW;IAsDzB;;OAEG;IACG,GAAG,CAAC,CAAC,EACT,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,UAAU,SAAI,GACb,OAAO,CAAC,CAAC,CAAC;IAiBb;;OAEG;IACG,SAAS,CAAC,CAAC,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,YAAY,CAAC,EAAE,YAAY,GAC1B,OAAO,CAAC,CAAC,CAAC;IAqCb;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAIxC;;OAEG;IACG,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,SAAI,GAAG,OAAO,CAAC,CAAC,CAAC;IAiB1E;;OAEG;IACG,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,SAAI,GAAG,OAAO,CAAC,CAAC,CAAC;IAiB7D;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;OAEG;IACH,QAAQ,IAAI;QACV,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;KACxB;CAOF"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AASD,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAG3B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,kBAAkB,CAAyB;IAGnD,OAAO,CAAC,mBAAmB,CAA8B;IAGzD,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAQ;IAG7C,OAAO,CAAC,QAAQ,CAA4C;gBAEhD,MAAM,EAAE,mBAAmB;IA+CvC;;;;;;;OAOG;YACW,gBAAgB;IAqB9B;;OAEG;YACW,YAAY;IA6C1B;;;OAGG;YACW,eAAe;IAiB7B;;OAEG;YACW,WAAW;IA4DzB;;OAEG;IACG,GAAG,CAAC,CAAC,EACT,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,UAAU,SAAI,GACb,OAAO,CAAC,CAAC,CAAC;IAiBb;;OAEG;IACG,SAAS,CAAC,CAAC,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,YAAY,CAAC,EAAE,YAAY,GAC1B,OAAO,CAAC,CAAC,CAAC;IAqCb;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAIxC;;OAEG;IACG,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,SAAI,GAAG,OAAO,CAAC,CAAC,CAAC;IAiB1E;;OAEG;IACG,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,SAAI,GAAG,OAAO,CAAC,CAAC,CAAC;IAiB7D;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;OAEG;IACH,QAAQ,IAAI;QACV,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;KACxB;CAOF"}