@procurementexpress.com/mcp 1.0.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 (40) hide show
  1. package/README.md +376 -0
  2. package/dist/api-client.d.ts +31 -0
  3. package/dist/api-client.js +108 -0
  4. package/dist/auth.d.ts +29 -0
  5. package/dist/auth.js +59 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +119 -0
  8. package/dist/tool-helpers.d.ts +34 -0
  9. package/dist/tool-helpers.js +26 -0
  10. package/dist/tools/approval-flows.d.ts +3 -0
  11. package/dist/tools/approval-flows.js +121 -0
  12. package/dist/tools/budgets.d.ts +3 -0
  13. package/dist/tools/budgets.js +73 -0
  14. package/dist/tools/comments.d.ts +3 -0
  15. package/dist/tools/comments.js +24 -0
  16. package/dist/tools/companies.d.ts +3 -0
  17. package/dist/tools/companies.js +67 -0
  18. package/dist/tools/departments.d.ts +3 -0
  19. package/dist/tools/departments.js +69 -0
  20. package/dist/tools/invoices.d.ts +3 -0
  21. package/dist/tools/invoices.js +132 -0
  22. package/dist/tools/payments.d.ts +3 -0
  23. package/dist/tools/payments.js +65 -0
  24. package/dist/tools/products.d.ts +3 -0
  25. package/dist/tools/products.js +57 -0
  26. package/dist/tools/purchase-orders.d.ts +3 -0
  27. package/dist/tools/purchase-orders.js +152 -0
  28. package/dist/tools/supplementary.d.ts +3 -0
  29. package/dist/tools/supplementary.js +70 -0
  30. package/dist/tools/suppliers.d.ts +3 -0
  31. package/dist/tools/suppliers.js +80 -0
  32. package/dist/tools/tax-rates.d.ts +3 -0
  33. package/dist/tools/tax-rates.js +44 -0
  34. package/dist/tools/users.d.ts +3 -0
  35. package/dist/tools/users.js +37 -0
  36. package/dist/tools/webhooks.d.ts +3 -0
  37. package/dist/tools/webhooks.js +61 -0
  38. package/dist/types.d.ts +732 -0
  39. package/dist/types.js +3 -0
  40. package/package.json +50 -0
package/README.md ADDED
@@ -0,0 +1,376 @@
1
+ # ProcurementExpress MCP Server
2
+
3
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that provides LLMs with access to the [ProcurementExpress](https://www.procurementexpress.com/) API. Manage purchase orders, invoices, budgets, suppliers, and procurement workflows through natural language.
4
+
5
+ ## Features
6
+
7
+ - **70+ tools** covering the full ProcurementExpress API surface
8
+ - **Dual API version support** — V1 (token-based) and V3 (OAuth2) authentication
9
+ - **Version-agnostic tool layer** — all tools work identically across API versions
10
+ - **Type-safe** — comprehensive TypeScript interfaces for all API entities
11
+ - **Zero external runtime dependencies** — only `@modelcontextprotocol/sdk` and `zod`
12
+
13
+ ## Quick Start
14
+
15
+ ### Prerequisites
16
+
17
+ - Node.js 18+
18
+ - A ProcurementExpress account with API access
19
+
20
+ ### Installation
21
+
22
+ No installation required — run directly with `npx`:
23
+
24
+ ```bash
25
+ npx -y @procurementexpress.com/mcp
26
+ ```
27
+
28
+ ### Usage with Claude Desktop
29
+
30
+ Add this to your Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json`):
31
+
32
+ ```json
33
+ {
34
+ "mcpServers": {
35
+ "procurementexpress": {
36
+ "command": "npx",
37
+ "args": ["-y", "@procurementexpress.com/mcp"],
38
+ "env": {
39
+ "PROCUREMENTEXPRESS_API_VERSION": "v1",
40
+ "PROCUREMENTEXPRESS_AUTH_TOKEN": "your_token",
41
+ "PROCUREMENTEXPRESS_COMPANY_ID": "your_company_id"
42
+ }
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ ### Usage with Claude Code
49
+
50
+ Add to your project's `.mcp.json`:
51
+
52
+ ```json
53
+ {
54
+ "mcpServers": {
55
+ "procurementexpress": {
56
+ "command": "npx",
57
+ "args": ["-y", "@procurementexpress.com/mcp"],
58
+ "env": {
59
+ "PROCUREMENTEXPRESS_API_VERSION": "v1",
60
+ "PROCUREMENTEXPRESS_AUTH_TOKEN": "your_token",
61
+ "PROCUREMENTEXPRESS_COMPANY_ID": "your_company_id"
62
+ }
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ ### Configuration
69
+
70
+ The server is configured entirely via environment variables (set them in the `env` block above).
71
+
72
+ #### V1 Authentication (Recommended)
73
+
74
+ Static token authentication. The token never expires.
75
+
76
+ | Variable | Value |
77
+ |----------|-------|
78
+ | `PROCUREMENTEXPRESS_API_VERSION` | `v1` |
79
+ | `PROCUREMENTEXPRESS_AUTH_TOKEN` | Your authentication token |
80
+ | `PROCUREMENTEXPRESS_COMPANY_ID` | Your company ID |
81
+
82
+ #### V3 Authentication (OAuth2)
83
+
84
+ OAuth2 password grant. Tokens are time-limited and require `client_id`/`client_secret`.
85
+
86
+ | Variable | Value |
87
+ |----------|-------|
88
+ | `PROCUREMENTEXPRESS_API_VERSION` | `v3` |
89
+ | `PROCUREMENTEXPRESS_CLIENT_ID` | Your OAuth2 client ID |
90
+ | `PROCUREMENTEXPRESS_CLIENT_SECRET` | Your OAuth2 client secret |
91
+
92
+ ## Authentication
93
+
94
+ The server supports two authentication modes, selected by the `PROCUREMENTEXPRESS_API_VERSION` environment variable.
95
+
96
+ ### V1 — Token-Based (Default)
97
+
98
+ Set `PROCUREMENTEXPRESS_API_VERSION=v1`. Provide your static token and company ID via environment variables or pass them to the `authenticate` tool at runtime.
99
+
100
+ - Sends `authentication_token` and `app_company_id` headers on every request
101
+ - Token never expires — no refresh logic needed
102
+ - Environment variables: `PROCUREMENTEXPRESS_AUTH_TOKEN`, `PROCUREMENTEXPRESS_COMPANY_ID`
103
+
104
+ ### V3 — OAuth2
105
+
106
+ Set `PROCUREMENTEXPRESS_API_VERSION=v3`. Requires `PROCUREMENTEXPRESS_CLIENT_ID` and `PROCUREMENTEXPRESS_CLIENT_SECRET` environment variables. Call the `authenticate` tool with email/password to obtain a Bearer token.
107
+
108
+ - Sends `Authorization: Bearer <token>` header on every request
109
+ - Tokens are time-limited with refresh support
110
+ - After authenticating, use `set_active_company` to select a company
111
+
112
+ ## Available Tools
113
+
114
+ ### Authentication (3 tools)
115
+
116
+ | Tool | Description |
117
+ |------|-------------|
118
+ | `authenticate` | V1: Set token + company ID. V3: OAuth2 login with email/password |
119
+ | `validate_token` | V1: Fetch current user to verify token. V3: Get token metadata |
120
+ | `revoke_token` | V1: Clear local token. V3: Revoke OAuth2 token |
121
+
122
+ ### Users (4 tools)
123
+
124
+ | Tool | Description |
125
+ |------|-------------|
126
+ | `get_current_user` | Get authenticated user's profile and company memberships |
127
+ | `update_current_user` | Update profile (email, name, phone, password) |
128
+ | `list_currencies` | List enabled currencies for the current company |
129
+ | `list_all_currencies` | List all available currencies globally |
130
+
131
+ ### Companies (7 tools)
132
+
133
+ | Tool | Description |
134
+ |------|-------------|
135
+ | `list_companies` | List all companies the current user belongs to |
136
+ | `get_company` | Get company details including settings and currencies |
137
+ | `set_active_company` | Set active company ID for subsequent API calls |
138
+ | `list_approvers` | List approvers filtered by department |
139
+ | `list_all_approvers` | List all approvers regardless of routing |
140
+ | `list_employees` | List all employees with roles |
141
+ | `invite_user` | Invite a user (roles: companyadmin, approver, finance, teammember) |
142
+
143
+ ### Budgets (4 tools)
144
+
145
+ | Tool | Description |
146
+ |------|-------------|
147
+ | `list_budgets` | List budgets with pagination, filter by department/archived/active |
148
+ | `get_budget` | Get budget details including remaining amount |
149
+ | `create_budget` | Create a new budget |
150
+ | `update_budget` | Update an existing budget |
151
+
152
+ ### Departments (4 tools)
153
+
154
+ | Tool | Description |
155
+ |------|-------------|
156
+ | `list_departments` | List departments with optional archived filter |
157
+ | `get_department` | Get a specific department |
158
+ | `create_department` | Create a new department |
159
+ | `update_department` | Update a department |
160
+
161
+ ### Suppliers (4 tools)
162
+
163
+ | Tool | Description |
164
+ |------|-------------|
165
+ | `list_suppliers` | List suppliers with pagination and filters |
166
+ | `get_supplier` | Get a specific supplier |
167
+ | `create_supplier` | Create a supplier (name must be unique) |
168
+ | `update_supplier` | Update a supplier |
169
+
170
+ ### Products (4 tools)
171
+
172
+ | Tool | Description |
173
+ |------|-------------|
174
+ | `list_products` | List products with supplier/archived filters |
175
+ | `get_product` | Get a specific product |
176
+ | `create_product` | Create a new product |
177
+ | `update_product` | Update a product |
178
+
179
+ ### Purchase Orders (10 tools)
180
+
181
+ | Tool | Description |
182
+ |------|-------------|
183
+ | `list_purchase_orders` | List POs with pagination and search |
184
+ | `get_purchase_order` | Get PO details with line items, comments, approvals |
185
+ | `create_purchase_order` | Create a PO (commit='Send' to submit, 'Draft' to save) |
186
+ | `approve_purchase_order` | Approve using the accept token from approver request |
187
+ | `reject_purchase_order` | Reject using the reject token from approver request |
188
+ | `override_and_approve_purchase_order` | Finance override approval (no token required) |
189
+ | `cancel_purchase_order` | Cancel a purchase order |
190
+ | `archive_purchase_order` | Archive a purchase order |
191
+ | `get_pending_request_count` | Get count of pending approval requests |
192
+ | `receive_purchase_order_items` | Mark items as received |
193
+
194
+ ### Invoices (10 tools)
195
+
196
+ | Tool | Description |
197
+ |------|-------------|
198
+ | `list_invoices` | List invoices with pagination and filters (100 per page) |
199
+ | `get_invoice` | Get invoice details |
200
+ | `create_invoice` | Create a new invoice |
201
+ | `update_invoice` | Update an existing invoice |
202
+ | `accept_invoice` | Accept an invoice awaiting review |
203
+ | `approve_invoice` | Approve an invoice |
204
+ | `reject_invoice` | Reject an invoice |
205
+ | `cancel_invoice` | Cancel an invoice |
206
+ | `archive_invoice` | Archive an invoice |
207
+ | `dearchive_invoice` | Restore an archived invoice |
208
+
209
+ ### Approval Flows (6 tools)
210
+
211
+ | Tool | Description |
212
+ |------|-------------|
213
+ | `list_approval_flows` | List approval flows with search and pagination |
214
+ | `get_approval_flow` | Get flow details with steps, approvers, conditions |
215
+ | `create_approval_flow` | Create a flow (document_type: 0=PO, 1=invoice) |
216
+ | `delete_approval_flow` | Delete an approval flow |
217
+ | `archive_approval_flow` | Archive an approval flow |
218
+ | `list_approval_flow_runs` | List runs with status and date filters |
219
+
220
+ ### Payments (2 tools)
221
+
222
+ | Tool | Description |
223
+ |------|-------------|
224
+ | `create_payment` | Create a payment (types: bank_transfer, card, check, cash, etc.) |
225
+ | `create_po_payment` | Create item-level payments for a purchase order |
226
+
227
+ ### Tax Rates (4 tools)
228
+
229
+ | Tool | Description |
230
+ |------|-------------|
231
+ | `list_tax_rates` | List all tax rates (single and combined) |
232
+ | `get_tax_rate` | Get a specific tax rate |
233
+ | `create_tax_rate` | Create a new tax rate |
234
+ | `update_tax_rate` | Update a tax rate |
235
+
236
+ ### Webhooks (4 tools)
237
+
238
+ | Tool | Description |
239
+ |------|-------------|
240
+ | `list_webhooks` | List webhooks with optional archived filter |
241
+ | `get_webhook` | Get a specific webhook |
242
+ | `create_webhook` | Create a webhook (events: new_po, po_approved, po_delivered, po_paid, po_cancelled, po_update) |
243
+ | `update_webhook` | Update a webhook |
244
+
245
+ ### Comments (2 tools)
246
+
247
+ | Tool | Description |
248
+ |------|-------------|
249
+ | `add_purchase_order_comment` | Add a comment to a purchase order |
250
+ | `add_invoice_comment` | Add a comment to an invoice |
251
+
252
+ ### Supplementary (5 tools)
253
+
254
+ | Tool | Description |
255
+ |------|-------------|
256
+ | `list_chart_of_accounts` | List chart of accounts with search |
257
+ | `list_qbo_customers` | List QuickBooks customers with search |
258
+ | `list_qbo_classes` | List QuickBooks classes with search |
259
+ | `list_send_to_supplier_templates` | List email templates for sending POs |
260
+ | `forward_purchase_order` | Email a PO to supplier(s) |
261
+
262
+ ## Project Structure
263
+
264
+ ```
265
+ src/
266
+ index.ts # Entry point — MCP server setup, auth tools, tool registration
267
+ api-client.ts # HTTP client with versioned path building and auth headers
268
+ auth.ts # Dual auth manager (V1 token / V3 OAuth2)
269
+ tool-helpers.ts # Shared response helpers and error handling wrapper
270
+ types.ts # TypeScript interfaces for all API entities
271
+ tools/
272
+ approval-flows.ts # Approval flow CRUD and run listing
273
+ budgets.ts # Budget CRUD
274
+ comments.ts # PO and invoice comments
275
+ companies.ts # Company details, employees, approvers, invitations
276
+ departments.ts # Department CRUD
277
+ invoices.ts # Invoice CRUD, approve/reject/cancel/archive
278
+ payments.ts # Payment creation (standalone and PO-linked)
279
+ products.ts # Product CRUD
280
+ purchase-orders.ts # PO CRUD, approve/reject/cancel/archive, receiving
281
+ supplementary.ts # Chart of accounts, QBO integration, email forwarding
282
+ suppliers.ts # Supplier CRUD
283
+ tax-rates.ts # Tax rate CRUD
284
+ users.ts # Current user profile and currency listing
285
+ webhooks.ts # Webhook CRUD
286
+ tests/
287
+ e2e/
288
+ setup.ts # MockApiServer with version-agnostic route registration
289
+ *.test.ts # E2E tests for each tool group (49 tests, 11 files)
290
+ ```
291
+
292
+ ## Development
293
+
294
+ For contributors who want to work on the server itself:
295
+
296
+ ```bash
297
+ git clone https://github.com/procurementexpress/mcp.git
298
+ cd mcp
299
+ npm install
300
+ ```
301
+
302
+ ### Build
303
+
304
+ ```bash
305
+ npm run build # Compile TypeScript to dist/
306
+ npm run dev # Watch mode for development
307
+ ```
308
+
309
+ ### Test
310
+
311
+ ```bash
312
+ npm test # Run all tests
313
+ npm run test:e2e # Run E2E tests only
314
+ npx vitest run tests/e2e/auth.test.ts # Run a single test file
315
+ npm run test:watch # Watch mode
316
+ ```
317
+
318
+ Tests use a `MockApiServer` — a lightweight HTTP server that simulates the ProcurementExpress API. Mock routes use version-agnostic regex patterns (`/api/v[13]/`) so tests work for both V1 and V3 configurations.
319
+
320
+ ### Adding a New Tool
321
+
322
+ 1. Add TypeScript interfaces to `src/types.ts` if needed
323
+ 2. Create or edit a file in `src/tools/` following the existing pattern:
324
+
325
+ ```typescript
326
+ import { z } from "zod";
327
+ import type { ApiClient } from "../api-client.js";
328
+ import type { Server } from "../tool-helpers.js";
329
+ import { jsonResponse, withErrorHandling } from "../tool-helpers.js";
330
+
331
+ export function registerMyTools(server: Server, apiClient: ApiClient): void {
332
+ server.registerTool(
333
+ "my_tool_name",
334
+ {
335
+ description: "What this tool does",
336
+ inputSchema: {
337
+ id: z.number().int().positive().describe("Resource ID"),
338
+ },
339
+ },
340
+ withErrorHandling(async (args) => {
341
+ const result = await apiClient.get(apiClient.buildPath(`/my_resource/${args.id}`));
342
+ return jsonResponse(result);
343
+ }),
344
+ );
345
+ }
346
+ ```
347
+
348
+ 3. Register the tool group in `src/index.ts`:
349
+
350
+ ```typescript
351
+ import { registerMyTools } from "./tools/my-tools.js";
352
+ registerMyTools(server, apiClient);
353
+ ```
354
+
355
+ 4. Add mock routes and tests in `tests/e2e/`
356
+
357
+ **Key conventions:**
358
+ - Always use `apiClient.buildPath("/resource")` — never hardcode `/api/v1/` or `/api/v3/`
359
+ - Wrap every handler with `withErrorHandling()`
360
+ - Use `jsonResponse()` for data and `textResponse()` for messages
361
+ - All imports must use `.js` extension (ES modules)
362
+
363
+ ## Environment Variables Reference
364
+
365
+ | Variable | Required | Default | Description |
366
+ |----------|----------|---------|-------------|
367
+ | `PROCUREMENTEXPRESS_API_BASE_URL` | No | `https://app.procurementexpress.com` | API base URL |
368
+ | `PROCUREMENTEXPRESS_API_VERSION` | No | `v1` | API version (`v1` or `v3`) |
369
+ | `PROCUREMENTEXPRESS_COMPANY_ID` | V1 | — | Company ID for V1 auth |
370
+ | `PROCUREMENTEXPRESS_AUTH_TOKEN` | V1 | — | Static authentication token for V1 |
371
+ | `PROCUREMENTEXPRESS_CLIENT_ID` | V3 | — | OAuth2 client ID for V3 |
372
+ | `PROCUREMENTEXPRESS_CLIENT_SECRET` | V3 | — | OAuth2 client secret for V3 |
373
+
374
+ ## License
375
+
376
+ ISC
@@ -0,0 +1,31 @@
1
+ export type ApiVersion = "v1" | "v3";
2
+ export declare class ApiClientError extends Error {
3
+ status: number;
4
+ constructor(status: number, message: string);
5
+ }
6
+ export declare class ApiClient {
7
+ private baseUrl;
8
+ private token;
9
+ private companyId;
10
+ private apiVersion;
11
+ private authMode;
12
+ constructor(baseUrl?: string, apiVersion?: ApiVersion);
13
+ getApiVersion(): ApiVersion;
14
+ setToken(token: string): void;
15
+ clearToken(): void;
16
+ getToken(): string | null;
17
+ setCompanyId(companyId: string): void;
18
+ getCompanyId(): string | null;
19
+ /**
20
+ * Build the full API path with the configured version prefix.
21
+ * Paths starting with /oauth or /api/ are used as-is.
22
+ * Paths like /budgets become /api/{version}/budgets.
23
+ */
24
+ buildPath(path: string): string;
25
+ private buildHeaders;
26
+ request<T>(method: string, path: string, body?: Record<string, unknown>, extraHeaders?: Record<string, string>): Promise<T>;
27
+ get<T>(path: string, extraHeaders?: Record<string, string>): Promise<T>;
28
+ post<T>(path: string, body?: Record<string, unknown>, extraHeaders?: Record<string, string>): Promise<T>;
29
+ put<T>(path: string, body?: Record<string, unknown>, extraHeaders?: Record<string, string>): Promise<T>;
30
+ delete<T>(path: string, extraHeaders?: Record<string, string>): Promise<T>;
31
+ }
@@ -0,0 +1,108 @@
1
+ export class ApiClientError extends Error {
2
+ status;
3
+ constructor(status, message) {
4
+ super(message);
5
+ this.status = status;
6
+ this.name = "ApiClientError";
7
+ }
8
+ }
9
+ export class ApiClient {
10
+ baseUrl;
11
+ token = null;
12
+ companyId = null;
13
+ apiVersion;
14
+ authMode;
15
+ constructor(baseUrl, apiVersion) {
16
+ this.baseUrl = (baseUrl ||
17
+ process.env.PROCUREMENTEXPRESS_API_BASE_URL ||
18
+ "https://app.procurementexpress.com").replace(/\/$/, "");
19
+ this.apiVersion = apiVersion || process.env.PROCUREMENTEXPRESS_API_VERSION || "v1";
20
+ this.authMode = this.apiVersion;
21
+ }
22
+ getApiVersion() {
23
+ return this.apiVersion;
24
+ }
25
+ setToken(token) {
26
+ this.token = token;
27
+ }
28
+ clearToken() {
29
+ this.token = null;
30
+ }
31
+ getToken() {
32
+ return this.token;
33
+ }
34
+ setCompanyId(companyId) {
35
+ this.companyId = companyId;
36
+ }
37
+ getCompanyId() {
38
+ return this.companyId;
39
+ }
40
+ /**
41
+ * Build the full API path with the configured version prefix.
42
+ * Paths starting with /oauth or /api/ are used as-is.
43
+ * Paths like /budgets become /api/{version}/budgets.
44
+ */
45
+ buildPath(path) {
46
+ if (path.startsWith("/oauth") || path.startsWith("/api/")) {
47
+ return path;
48
+ }
49
+ return `/api/${this.apiVersion}${path}`;
50
+ }
51
+ buildHeaders(extraHeaders) {
52
+ const headers = {
53
+ "Content-Type": "application/json",
54
+ };
55
+ if (this.token) {
56
+ if (this.authMode === "v1") {
57
+ headers["authentication_token"] = this.token;
58
+ }
59
+ else {
60
+ headers["Authorization"] = `Bearer ${this.token}`;
61
+ }
62
+ }
63
+ if (this.companyId) {
64
+ headers["app_company_id"] = this.companyId;
65
+ }
66
+ if (extraHeaders) {
67
+ Object.assign(headers, extraHeaders);
68
+ }
69
+ return headers;
70
+ }
71
+ async request(method, path, body, extraHeaders) {
72
+ const url = `${this.baseUrl}${path}`;
73
+ const headers = this.buildHeaders(extraHeaders);
74
+ const options = { method, headers };
75
+ if (body && (method === "POST" || method === "PUT" || method === "PATCH")) {
76
+ options.body = JSON.stringify(body);
77
+ }
78
+ const response = await fetch(url, options);
79
+ if (!response.ok) {
80
+ let message;
81
+ try {
82
+ const errorBody = (await response.json());
83
+ message = errorBody.message || response.statusText;
84
+ }
85
+ catch {
86
+ message = response.statusText;
87
+ }
88
+ throw new ApiClientError(response.status, `${response.status}: ${message}`);
89
+ }
90
+ // Handle 204 No Content
91
+ if (response.status === 204) {
92
+ return {};
93
+ }
94
+ return (await response.json());
95
+ }
96
+ async get(path, extraHeaders) {
97
+ return this.request("GET", path, undefined, extraHeaders);
98
+ }
99
+ async post(path, body, extraHeaders) {
100
+ return this.request("POST", path, body, extraHeaders);
101
+ }
102
+ async put(path, body, extraHeaders) {
103
+ return this.request("PUT", path, body, extraHeaders);
104
+ }
105
+ async delete(path, extraHeaders) {
106
+ return this.request("DELETE", path, undefined, extraHeaders);
107
+ }
108
+ }
package/dist/auth.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { ApiClient } from "./api-client.js";
2
+ import type { OAuthTokenResponse, TokenInfo, User } from "./types.js";
3
+ export declare class AuthManager {
4
+ private apiClient;
5
+ private clientId;
6
+ private clientSecret;
7
+ constructor(apiClient: ApiClient, clientId?: string, clientSecret?: string);
8
+ isV1(): boolean;
9
+ /**
10
+ * V1 authentication: Set the static auth token and company ID directly.
11
+ */
12
+ authenticateV1(authToken: string, companyId: string): void;
13
+ /**
14
+ * V3 authentication: OAuth2 password grant.
15
+ */
16
+ authenticateV3(email: string, password: string): Promise<OAuthTokenResponse>;
17
+ /**
18
+ * Validate the current token.
19
+ * V1: Calls /api/v1/currentuser to verify the token works.
20
+ * V3: Calls /oauth/token/info for token metadata.
21
+ */
22
+ validateToken(): Promise<TokenInfo | User>;
23
+ /**
24
+ * Revoke the current token.
25
+ * V1: Just clears the local token (V1 tokens are static).
26
+ * V3: Calls /oauth/revoke then clears the local token.
27
+ */
28
+ revokeToken(): Promise<void>;
29
+ }
package/dist/auth.js ADDED
@@ -0,0 +1,59 @@
1
+ export class AuthManager {
2
+ apiClient;
3
+ clientId;
4
+ clientSecret;
5
+ constructor(apiClient, clientId, clientSecret) {
6
+ this.apiClient = apiClient;
7
+ this.clientId = clientId || process.env.PROCUREMENTEXPRESS_CLIENT_ID || "";
8
+ this.clientSecret = clientSecret || process.env.PROCUREMENTEXPRESS_CLIENT_SECRET || "";
9
+ }
10
+ isV1() {
11
+ return this.apiClient.getApiVersion() === "v1";
12
+ }
13
+ /**
14
+ * V1 authentication: Set the static auth token and company ID directly.
15
+ */
16
+ authenticateV1(authToken, companyId) {
17
+ this.apiClient.setToken(authToken);
18
+ this.apiClient.setCompanyId(companyId);
19
+ }
20
+ /**
21
+ * V3 authentication: OAuth2 password grant.
22
+ */
23
+ async authenticateV3(email, password) {
24
+ const response = await this.apiClient.post("/oauth/token", {
25
+ email,
26
+ password,
27
+ grant_type: "password",
28
+ client_id: this.clientId,
29
+ client_secret: this.clientSecret,
30
+ });
31
+ this.apiClient.setToken(response.access_token);
32
+ return response;
33
+ }
34
+ /**
35
+ * Validate the current token.
36
+ * V1: Calls /api/v1/currentuser to verify the token works.
37
+ * V3: Calls /oauth/token/info for token metadata.
38
+ */
39
+ async validateToken() {
40
+ if (this.isV1()) {
41
+ return this.apiClient.get(this.apiClient.buildPath("/currentuser"));
42
+ }
43
+ return this.apiClient.get("/oauth/token/info");
44
+ }
45
+ /**
46
+ * Revoke the current token.
47
+ * V1: Just clears the local token (V1 tokens are static).
48
+ * V3: Calls /oauth/revoke then clears the local token.
49
+ */
50
+ async revokeToken() {
51
+ if (!this.isV1()) {
52
+ await this.apiClient.post("/oauth/revoke", {
53
+ client_id: this.clientId,
54
+ client_secret: this.clientSecret,
55
+ });
56
+ }
57
+ this.apiClient.clearToken();
58
+ }
59
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};