supporthero-mcp-server 1.0.1 → 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.
- package/README.md +36 -6
- package/dist/index.js +29 -4
- package/dist/index.js.map +1 -1
- package/dist/schemas/inputs.d.ts +29 -0
- package/dist/schemas/inputs.d.ts.map +1 -1
- package/dist/schemas/inputs.js +39 -0
- package/dist/schemas/inputs.js.map +1 -1
- package/dist/services/client.d.ts +25 -3
- package/dist/services/client.d.ts.map +1 -1
- package/dist/services/client.js +84 -2
- package/dist/services/client.js.map +1 -1
- package/dist/services/formatting.d.ts +3 -1
- package/dist/services/formatting.d.ts.map +1 -1
- package/dist/services/formatting.js +20 -0
- package/dist/services/formatting.js.map +1 -1
- package/dist/session.d.ts +46 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +198 -0
- package/dist/session.js.map +1 -0
- package/dist/tools/helpCenter.d.ts +5 -0
- package/dist/tools/helpCenter.d.ts.map +1 -1
- package/dist/tools/helpCenter.js +84 -2
- package/dist/tools/helpCenter.js.map +1 -1
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +13 -5
- package/src/constants.ts +0 -5
- package/src/index.ts +0 -49
- package/src/schemas/inputs.ts +0 -85
- package/src/services/client.ts +0 -96
- package/src/services/formatting.ts +0 -74
- package/src/tools/helpCenter.ts +0 -320
- package/src/types.ts +0 -50
- package/tsconfig.json +0 -19
package/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# SupportHero MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An MCP server for the SupportHero Help Center API. Enables Claude to search, browse, read, and update your help center articles directly in conversation.
|
|
4
4
|
|
|
5
5
|
## Tools
|
|
6
6
|
|
|
7
|
+
### Read Tools (always available)
|
|
8
|
+
|
|
7
9
|
| Tool | Description |
|
|
8
10
|
|------|-------------|
|
|
9
11
|
| `supporthero_list_categories` | List top-level or sub-categories |
|
|
@@ -12,7 +14,13 @@ A read-only MCP server for the SupportHero Help Center API. Enables Claude to se
|
|
|
12
14
|
| `supporthero_get_article` | Get full article content by ID |
|
|
13
15
|
| `supporthero_search_articles` | Full-text search across all articles |
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
### Write Tools (requires login credentials)
|
|
18
|
+
|
|
19
|
+
| Tool | Description |
|
|
20
|
+
|------|-------------|
|
|
21
|
+
| `supporthero_update_article` | Update an article's title, content, keywords, publication status, or category |
|
|
22
|
+
|
|
23
|
+
Write tools are only registered when `SUPPORTHERO_EMAIL` and `SUPPORTHERO_PASSWORD` are provided. Without these, the server operates in read-only mode exactly as before.
|
|
16
24
|
|
|
17
25
|
## Setup
|
|
18
26
|
|
|
@@ -40,6 +48,8 @@ Add this to your Claude Desktop config file:
|
|
|
40
48
|
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
41
49
|
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
42
50
|
|
|
51
|
+
Read-only mode (API key only):
|
|
52
|
+
|
|
43
53
|
```json
|
|
44
54
|
{
|
|
45
55
|
"mcpServers": {
|
|
@@ -55,10 +65,26 @@ Add this to your Claude Desktop config file:
|
|
|
55
65
|
}
|
|
56
66
|
```
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
With write access (add login credentials):
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"mcpServers": {
|
|
73
|
+
"supporthero": {
|
|
74
|
+
"command": "node",
|
|
75
|
+
"args": ["/absolute/path/to/supporthero-mcp-server/dist/index.js"],
|
|
76
|
+
"env": {
|
|
77
|
+
"SUPPORTHERO_DOMAIN": "https://yourcompany.supporthero.io",
|
|
78
|
+
"SUPPORTHERO_API_KEY": "your-api-key-here",
|
|
79
|
+
"SUPPORTHERO_EMAIL": "your-login-email",
|
|
80
|
+
"SUPPORTHERO_PASSWORD": "your-login-password"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Replace the placeholder values with your actual credentials.
|
|
62
88
|
|
|
63
89
|
### 5. Restart Claude Desktop
|
|
64
90
|
|
|
@@ -72,6 +98,8 @@ Once connected, you can ask Claude things like:
|
|
|
72
98
|
- "Show me the full content of our booking policy article"
|
|
73
99
|
- "List all categories in the help center"
|
|
74
100
|
- "Search our help center for instructor qualifications"
|
|
101
|
+
- "Update the booking policy article to mention our new 24-hour cancellation window"
|
|
102
|
+
- "Unpublish the outdated pricing article"
|
|
75
103
|
|
|
76
104
|
## Environment Variables
|
|
77
105
|
|
|
@@ -79,3 +107,5 @@ Once connected, you can ask Claude things like:
|
|
|
79
107
|
|----------|----------|-------------|
|
|
80
108
|
| `SUPPORTHERO_DOMAIN` | Yes | Your Help Center URL (e.g. `https://propel.supporthero.io`) |
|
|
81
109
|
| `SUPPORTHERO_API_KEY` | Yes | API key from SupportHero Settings page |
|
|
110
|
+
| `SUPPORTHERO_EMAIL` | No | Login email for write access |
|
|
111
|
+
| `SUPPORTHERO_PASSWORD` | No | Login password for write access |
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { SupportHeroClient } from "./services/client.js";
|
|
5
|
-
import {
|
|
5
|
+
import { SessionManager } from "./session.js";
|
|
6
|
+
import { registerTools, registerWriteTools } from "./tools/helpCenter.js";
|
|
6
7
|
// ── Configuration ───────────────────────────────────────────
|
|
7
8
|
const SUPPORTHERO_DOMAIN = process.env.SUPPORTHERO_DOMAIN;
|
|
8
9
|
const SUPPORTHERO_API_KEY = process.env.SUPPORTHERO_API_KEY;
|
|
10
|
+
const SUPPORTHERO_EMAIL = process.env.SUPPORTHERO_EMAIL;
|
|
11
|
+
const SUPPORTHERO_PASSWORD = process.env.SUPPORTHERO_PASSWORD;
|
|
9
12
|
if (!SUPPORTHERO_DOMAIN) {
|
|
10
13
|
console.error("Missing SUPPORTHERO_DOMAIN env var. " +
|
|
11
14
|
"Set it to your Help Center URL, e.g. https://yourcompany.supporthero.io");
|
|
@@ -16,18 +19,40 @@ if (!SUPPORTHERO_API_KEY) {
|
|
|
16
19
|
"Find your API key in SupportHero > Settings (bottom of page).");
|
|
17
20
|
process.exit(1);
|
|
18
21
|
}
|
|
22
|
+
// ── Session (optional, for write access) ────────────────────
|
|
23
|
+
const writeEnabled = !!(SUPPORTHERO_EMAIL && SUPPORTHERO_PASSWORD);
|
|
24
|
+
let session;
|
|
25
|
+
if (writeEnabled) {
|
|
26
|
+
session = new SessionManager(SUPPORTHERO_EMAIL, SUPPORTHERO_PASSWORD, SUPPORTHERO_DOMAIN);
|
|
27
|
+
}
|
|
19
28
|
// ── Server Setup ────────────────────────────────────────────
|
|
20
29
|
const server = new McpServer({
|
|
21
30
|
name: "supporthero-mcp-server",
|
|
22
|
-
version: "1.
|
|
31
|
+
version: "1.1.0",
|
|
23
32
|
});
|
|
24
|
-
const client = new SupportHeroClient(SUPPORTHERO_DOMAIN, SUPPORTHERO_API_KEY);
|
|
33
|
+
const client = new SupportHeroClient(SUPPORTHERO_DOMAIN, SUPPORTHERO_API_KEY, session);
|
|
25
34
|
registerTools(server, client);
|
|
35
|
+
if (writeEnabled) {
|
|
36
|
+
registerWriteTools(server, client);
|
|
37
|
+
}
|
|
26
38
|
// ── Transport ───────────────────────────────────────────────
|
|
27
39
|
async function main() {
|
|
40
|
+
// Verify write credentials at startup so bad config fails fast
|
|
41
|
+
// rather than silently succeeding until the first write attempt.
|
|
42
|
+
if (session) {
|
|
43
|
+
try {
|
|
44
|
+
await session.login();
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error("Failed to authenticate with SupportHero for write access. " +
|
|
48
|
+
"Check SUPPORTHERO_EMAIL and SUPPORTHERO_PASSWORD.\n" +
|
|
49
|
+
`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
28
53
|
const transport = new StdioServerTransport();
|
|
29
54
|
await server.connect(transport);
|
|
30
|
-
console.error(
|
|
55
|
+
console.error(`SupportHero MCP server running on stdio (write tools: ${writeEnabled ? "enabled" : "disabled"})`);
|
|
31
56
|
}
|
|
32
57
|
main().catch((error) => {
|
|
33
58
|
console.error("Fatal error:", error);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE1E,+DAA+D;AAE/D,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;AAC1D,MAAM,mBAAmB,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;AAC5D,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;AACxD,MAAM,oBAAoB,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;AAE9D,IAAI,CAAC,kBAAkB,EAAE,CAAC;IACxB,OAAO,CAAC,KAAK,CACX,sCAAsC;QACtC,yEAAyE,CAC1E,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,CAAC,mBAAmB,EAAE,CAAC;IACzB,OAAO,CAAC,KAAK,CACX,uCAAuC;QACvC,+DAA+D,CAChE,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,+DAA+D;AAE/D,MAAM,YAAY,GAAG,CAAC,CAAC,CAAC,iBAAiB,IAAI,oBAAoB,CAAC,CAAC;AACnE,IAAI,OAAmC,CAAC;AAExC,IAAI,YAAY,EAAE,CAAC;IACjB,OAAO,GAAG,IAAI,cAAc,CAAC,iBAAkB,EAAE,oBAAqB,EAAE,kBAAkB,CAAC,CAAC;AAC9F,CAAC;AAED,+DAA+D;AAE/D,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,wBAAwB;IAC9B,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,kBAAkB,EAAE,mBAAmB,EAAE,OAAO,CAAC,CAAC;AAEvF,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9B,IAAI,YAAY,EAAE,CAAC;IACjB,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACrC,CAAC;AAED,+DAA+D;AAE/D,KAAK,UAAU,IAAI;IACjB,+DAA+D;IAC/D,iEAAiE;IACjE,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CACX,4DAA4D;gBAC5D,qDAAqD;gBACrD,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACnE,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CACX,yDAAyD,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,GAAG,CAClG,CAAC;AACJ,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/schemas/inputs.d.ts
CHANGED
|
@@ -45,4 +45,33 @@ export declare const SearchArticlesInputSchema: z.ZodObject<{
|
|
|
45
45
|
offset?: string | undefined;
|
|
46
46
|
}>;
|
|
47
47
|
export type SearchArticlesInput = z.infer<typeof SearchArticlesInputSchema>;
|
|
48
|
+
export declare const UpdateArticleInputSchema: z.ZodObject<{
|
|
49
|
+
article_id: z.ZodString;
|
|
50
|
+
title: z.ZodOptional<z.ZodString>;
|
|
51
|
+
description: z.ZodOptional<z.ZodString>;
|
|
52
|
+
keywords: z.ZodOptional<z.ZodString>;
|
|
53
|
+
meta_keywords: z.ZodOptional<z.ZodString>;
|
|
54
|
+
published: z.ZodOptional<z.ZodBoolean>;
|
|
55
|
+
container_id: z.ZodOptional<z.ZodString>;
|
|
56
|
+
type: z.ZodOptional<z.ZodEnum<["article", "question"]>>;
|
|
57
|
+
}, "strict", z.ZodTypeAny, {
|
|
58
|
+
article_id: string;
|
|
59
|
+
title?: string | undefined;
|
|
60
|
+
description?: string | undefined;
|
|
61
|
+
keywords?: string | undefined;
|
|
62
|
+
published?: boolean | undefined;
|
|
63
|
+
type?: "article" | "question" | undefined;
|
|
64
|
+
meta_keywords?: string | undefined;
|
|
65
|
+
container_id?: string | undefined;
|
|
66
|
+
}, {
|
|
67
|
+
article_id: string;
|
|
68
|
+
title?: string | undefined;
|
|
69
|
+
description?: string | undefined;
|
|
70
|
+
keywords?: string | undefined;
|
|
71
|
+
published?: boolean | undefined;
|
|
72
|
+
type?: "article" | "question" | undefined;
|
|
73
|
+
meta_keywords?: string | undefined;
|
|
74
|
+
container_id?: string | undefined;
|
|
75
|
+
}>;
|
|
76
|
+
export type UpdateArticleInput = z.infer<typeof UpdateArticleInputSchema>;
|
|
48
77
|
//# sourceMappingURL=inputs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../src/schemas/inputs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,yBAAyB;;;;;;EAU3B,CAAC;AAEZ,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,sBAAsB;;;;;;EAOxB,CAAC;AAEZ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAItE,eAAO,MAAM,uBAAuB;;;;;;EAUzB,CAAC;AAEZ,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,qBAAqB;;;;;;EAOvB,CAAC;AAEZ,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAIpE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;EAuB3B,CAAC;AAEZ,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../src/schemas/inputs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,yBAAyB;;;;;;EAU3B,CAAC;AAEZ,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,sBAAsB;;;;;;EAOxB,CAAC;AAEZ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAItE,eAAO,MAAM,uBAAuB;;;;;;EAUzB,CAAC;AAEZ,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,qBAAqB;;;;;;EAOvB,CAAC;AAEZ,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAIpE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;EAuB3B,CAAC;AAEZ,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAI5E,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyC1B,CAAC;AAEZ,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
|
package/dist/schemas/inputs.js
CHANGED
|
@@ -57,4 +57,43 @@ export const SearchArticlesInputSchema = z
|
|
|
57
57
|
"Pass the offset value from the prior result to get the next page."),
|
|
58
58
|
})
|
|
59
59
|
.strict();
|
|
60
|
+
// ── Write Operations ───────────────────────────────────────
|
|
61
|
+
export const UpdateArticleInputSchema = z
|
|
62
|
+
.object({
|
|
63
|
+
article_id: z
|
|
64
|
+
.string()
|
|
65
|
+
.min(1, "article_id is required")
|
|
66
|
+
.describe("The ID of the article to update."),
|
|
67
|
+
title: z
|
|
68
|
+
.string()
|
|
69
|
+
.optional()
|
|
70
|
+
.describe("New title for the article."),
|
|
71
|
+
description: z
|
|
72
|
+
.string()
|
|
73
|
+
.optional()
|
|
74
|
+
.describe("New HTML body content for the article. " +
|
|
75
|
+
"Use standard HTML tags (p, h1-h6, ul, ol, li, a, strong, em, etc.)."),
|
|
76
|
+
keywords: z
|
|
77
|
+
.string()
|
|
78
|
+
.optional()
|
|
79
|
+
.describe("Newline-separated keywords for the article."),
|
|
80
|
+
meta_keywords: z
|
|
81
|
+
.string()
|
|
82
|
+
.optional()
|
|
83
|
+
.describe("Comma-separated meta keywords for SEO."),
|
|
84
|
+
published: z
|
|
85
|
+
.boolean()
|
|
86
|
+
.optional()
|
|
87
|
+
.describe("Set to true to publish, false to unpublish."),
|
|
88
|
+
container_id: z
|
|
89
|
+
.string()
|
|
90
|
+
.optional()
|
|
91
|
+
.describe("Category ID to move the article to. " +
|
|
92
|
+
"Use supporthero_list_categories to find valid category IDs."),
|
|
93
|
+
type: z
|
|
94
|
+
.enum(["article", "question"])
|
|
95
|
+
.optional()
|
|
96
|
+
.describe('Article type: "article" or "question".'),
|
|
97
|
+
})
|
|
98
|
+
.strict();
|
|
60
99
|
//# sourceMappingURL=inputs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inputs.js","sourceRoot":"","sources":["../../src/schemas/inputs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEzE,+DAA+D;AAE/D,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC;KACvC,MAAM,CAAC;IACN,kBAAkB,EAAE,CAAC;SAClB,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,wDAAwD;QACxD,+CAA+C,CAChD;CACJ,CAAC;KACD,MAAM,EAAE,CAAC;AAIZ,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC;KACpC,MAAM,CAAC;IACN,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,yBAAyB,CAAC;SACjC,QAAQ,CAAC,qCAAqC,CAAC;CACnD,CAAC;KACD,MAAM,EAAE,CAAC;AAIZ,+DAA+D;AAE/D,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC;KACrC,MAAM,CAAC;IACN,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,yBAAyB,CAAC;SACjC,QAAQ,CACP,iDAAiD;QACjD,6DAA6D,CAC9D;CACJ,CAAC;KACD,MAAM,EAAE,CAAC;AAIZ,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC;KACnC,MAAM,CAAC;IACN,UAAU,EAAE,CAAC;SACV,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,wBAAwB,CAAC;SAChC,QAAQ,CAAC,0DAA0D,CAAC;CACxE,CAAC;KACD,MAAM,EAAE,CAAC;AAIZ,+DAA+D;AAE/D,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC;KACvC,MAAM,CAAC;IACN,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,yBAAyB,CAAC;SACjC,QAAQ,CAAC,+DAA+D,CAAC;IAC5E,GAAG,EAAE,CAAC;SACH,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,kBAAkB,CAAC;SACvB,OAAO,CAAC,kBAAkB,CAAC;SAC3B,QAAQ,CACP,sDAAsD,kBAAkB,aAAa,kBAAkB,IAAI,CAC5G;IACH,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,qDAAqD;QACrD,mEAAmE,CACpE;CACJ,CAAC;KACD,MAAM,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"inputs.js","sourceRoot":"","sources":["../../src/schemas/inputs.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import type { SHCategory, SHArticle, SHSearchResponse } from "../types.js";
|
|
1
|
+
import type { SHCategory, SHArticle, SHSearchResponse, SHArticleUpdatePayload, SHArticleUpdateFields } from "../types.js";
|
|
2
|
+
import type { SessionManager } from "../session.js";
|
|
2
3
|
/**
|
|
3
4
|
* SupportHero API client.
|
|
4
|
-
*
|
|
5
|
+
* Read methods use the public API key. Write methods require a SessionManager
|
|
6
|
+
* with authenticated session cookies.
|
|
5
7
|
*/
|
|
6
8
|
export declare class SupportHeroClient {
|
|
7
9
|
private baseUrl;
|
|
8
10
|
private apiKey;
|
|
9
|
-
|
|
11
|
+
private session;
|
|
12
|
+
constructor(baseUrl: string, apiKey: string, session?: SessionManager);
|
|
13
|
+
/** Whether this client has write capability (session credentials provided). */
|
|
14
|
+
get hasWriteAccess(): boolean;
|
|
10
15
|
/**
|
|
11
16
|
* Core fetch wrapper. Appends apiKey and handles errors uniformly.
|
|
12
17
|
*/
|
|
@@ -21,5 +26,22 @@ export declare class SupportHeroClient {
|
|
|
21
26
|
getArticle(articleId: string): Promise<SHArticle>;
|
|
22
27
|
/** Full-text search across all articles. Returns paginated results. */
|
|
23
28
|
searchArticles(searchText: string, max?: number, offset?: string): Promise<SHSearchResponse>;
|
|
29
|
+
/**
|
|
30
|
+
* Ensure we have a session before attempting write operations.
|
|
31
|
+
*/
|
|
32
|
+
private requireSession;
|
|
33
|
+
/**
|
|
34
|
+
* GET an article via the authenticated session endpoint.
|
|
35
|
+
* Uses /api/article/{id} (no v1 prefix) which may return additional
|
|
36
|
+
* fields needed for the PUT payload.
|
|
37
|
+
*/
|
|
38
|
+
getArticleForUpdate(articleId: string): Promise<SHArticleUpdatePayload>;
|
|
39
|
+
/**
|
|
40
|
+
* Update an article by merging provided fields into the existing article
|
|
41
|
+
* and PUTting the full payload.
|
|
42
|
+
*
|
|
43
|
+
* Flow: GET current article -> merge updates -> PUT full object
|
|
44
|
+
*/
|
|
45
|
+
updateArticle(articleId: string, updates: SHArticleUpdateFields): Promise<SHArticleUpdatePayload>;
|
|
24
46
|
}
|
|
25
47
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/services/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EAEV,SAAS,EAET,gBAAgB,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/services/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EAEV,SAAS,EAET,gBAAgB,EAChB,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAwB;gBAE3B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc;IAOrE,+EAA+E;IAC/E,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED;;OAEG;YACW,OAAO;IA0BrB,sEAAsE;IAChE,cAAc,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAStE,mCAAmC;IAC7B,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAM1D,6CAA6C;IACvC,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAO5D,kCAAkC;IAC5B,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAMvD,uEAAuE;IACjE,cAAc,CAClB,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,gBAAgB,CAAC;IAS5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;;;OAIG;IACG,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAiB7E;;;;;OAKG;IACG,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,sBAAsB,CAAC;CAmCnC"}
|
package/dist/services/client.js
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { API_VERSION } from "../constants.js";
|
|
2
2
|
/**
|
|
3
3
|
* SupportHero API client.
|
|
4
|
-
*
|
|
4
|
+
* Read methods use the public API key. Write methods require a SessionManager
|
|
5
|
+
* with authenticated session cookies.
|
|
5
6
|
*/
|
|
6
7
|
export class SupportHeroClient {
|
|
7
8
|
baseUrl;
|
|
8
9
|
apiKey;
|
|
9
|
-
|
|
10
|
+
session;
|
|
11
|
+
constructor(baseUrl, apiKey, session) {
|
|
10
12
|
// Normalize: strip trailing slash
|
|
11
13
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
12
14
|
this.apiKey = apiKey;
|
|
15
|
+
this.session = session ?? null;
|
|
16
|
+
}
|
|
17
|
+
/** Whether this client has write capability (session credentials provided). */
|
|
18
|
+
get hasWriteAccess() {
|
|
19
|
+
return this.session !== null;
|
|
13
20
|
}
|
|
14
21
|
/**
|
|
15
22
|
* Core fetch wrapper. Appends apiKey and handles errors uniformly.
|
|
@@ -68,5 +75,80 @@ export class SupportHeroClient {
|
|
|
68
75
|
params.offset = offset;
|
|
69
76
|
return this.request("search", params);
|
|
70
77
|
}
|
|
78
|
+
// ── Write Operations ──────────────────────────────────────
|
|
79
|
+
/**
|
|
80
|
+
* Ensure we have a session before attempting write operations.
|
|
81
|
+
*/
|
|
82
|
+
requireSession() {
|
|
83
|
+
if (!this.session) {
|
|
84
|
+
throw new Error("Write operations require session authentication. " +
|
|
85
|
+
"Provide SUPPORTHERO_EMAIL and SUPPORTHERO_PASSWORD env vars.");
|
|
86
|
+
}
|
|
87
|
+
return this.session;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* GET an article via the authenticated session endpoint.
|
|
91
|
+
* Uses /api/article/{id} (no v1 prefix) which may return additional
|
|
92
|
+
* fields needed for the PUT payload.
|
|
93
|
+
*/
|
|
94
|
+
async getArticleForUpdate(articleId) {
|
|
95
|
+
const session = this.requireSession();
|
|
96
|
+
const url = `${this.baseUrl}/api/article/${articleId}`;
|
|
97
|
+
const response = await session.authenticatedFetch(url, {
|
|
98
|
+
headers: { "Accept": "application/json" },
|
|
99
|
+
});
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
const body = await response.text().catch(() => "");
|
|
102
|
+
throw new Error(`Failed to fetch article ${articleId} for update: ${response.status} ${response.statusText}. ${body}`);
|
|
103
|
+
}
|
|
104
|
+
return response.json();
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Update an article by merging provided fields into the existing article
|
|
108
|
+
* and PUTting the full payload.
|
|
109
|
+
*
|
|
110
|
+
* Flow: GET current article -> merge updates -> PUT full object
|
|
111
|
+
*/
|
|
112
|
+
async updateArticle(articleId, updates) {
|
|
113
|
+
const session = this.requireSession();
|
|
114
|
+
// Fetch the current state of the article
|
|
115
|
+
const current = await this.getArticleForUpdate(articleId);
|
|
116
|
+
// Merge user-provided updates into the full payload
|
|
117
|
+
const cleaned = stripUndefined(updates);
|
|
118
|
+
const payload = {
|
|
119
|
+
...current,
|
|
120
|
+
...cleaned,
|
|
121
|
+
};
|
|
122
|
+
// Ensure the id matches
|
|
123
|
+
payload.id = articleId;
|
|
124
|
+
const url = `${this.baseUrl}/api/article/${articleId}`;
|
|
125
|
+
const response = await session.authenticatedFetch(url, {
|
|
126
|
+
method: "PUT",
|
|
127
|
+
headers: {
|
|
128
|
+
"Content-Type": "application/json",
|
|
129
|
+
"Accept": "application/json",
|
|
130
|
+
},
|
|
131
|
+
body: JSON.stringify(payload),
|
|
132
|
+
});
|
|
133
|
+
if (!response.ok) {
|
|
134
|
+
const body = await response.text().catch(() => "");
|
|
135
|
+
throw new Error(`Failed to update article ${articleId}: ${response.status} ${response.statusText}. ${body}`);
|
|
136
|
+
}
|
|
137
|
+
return response.json();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Remove undefined values from an object so they don't overwrite
|
|
142
|
+
* existing fields during the spread merge.
|
|
143
|
+
*/
|
|
144
|
+
function stripUndefined(obj) {
|
|
145
|
+
const result = {};
|
|
146
|
+
for (const key of Object.keys(obj)) {
|
|
147
|
+
if (obj[key] !== undefined) {
|
|
148
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
149
|
+
result[key] = obj[key];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
71
153
|
}
|
|
72
154
|
//# sourceMappingURL=client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/services/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/services/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SHCategory, SHArticle } from "../types.js";
|
|
1
|
+
import type { SHCategory, SHArticle, SHArticleUpdatePayload } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Strip HTML tags from article body to produce plain text.
|
|
4
4
|
* Keeps it simple since we just need readable content for the LLM.
|
|
@@ -8,6 +8,8 @@ export declare function stripHtml(html: string): string;
|
|
|
8
8
|
export declare function formatCategory(cat: SHCategory): string;
|
|
9
9
|
/** Format a single article for display. Optionally include body content. */
|
|
10
10
|
export declare function formatArticle(article: SHArticle, includeBody?: boolean): string;
|
|
11
|
+
/** Format the result of an article update, highlighting which fields were changed. */
|
|
12
|
+
export declare function formatArticleUpdate(article: SHArticleUpdatePayload, changedFields: string[]): string;
|
|
11
13
|
/** Truncate text if it exceeds the character limit, with a warning. */
|
|
12
14
|
export declare function truncateIfNeeded(text: string): string;
|
|
13
15
|
//# sourceMappingURL=formatting.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatting.d.ts","sourceRoot":"","sources":["../../src/services/formatting.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"formatting.d.ts","sourceRoot":"","sources":["../../src/services/formatting.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGjF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAe9C;AAED,4CAA4C;AAC5C,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAYtD;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,GAAE,OAAe,GAAG,MAAM,CA0BtF;AAED,sFAAsF;AACtF,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,sBAAsB,EAC/B,aAAa,EAAE,MAAM,EAAE,GACtB,MAAM,CAqBR;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIrD"}
|
|
@@ -58,6 +58,26 @@ export function formatArticle(article, includeBody = false) {
|
|
|
58
58
|
}
|
|
59
59
|
return lines.join("\n");
|
|
60
60
|
}
|
|
61
|
+
/** Format the result of an article update, highlighting which fields were changed. */
|
|
62
|
+
export function formatArticleUpdate(article, changedFields) {
|
|
63
|
+
const lines = [
|
|
64
|
+
`Article updated successfully.`,
|
|
65
|
+
``,
|
|
66
|
+
`Title: ${article.title} (ID: ${article.id})`,
|
|
67
|
+
` Category ID: ${article.containerId}`,
|
|
68
|
+
` Type: ${article.type}`,
|
|
69
|
+
` Published: ${article.published ? "yes" : "no"}`,
|
|
70
|
+
];
|
|
71
|
+
if (article.keywords) {
|
|
72
|
+
lines.push(` Keywords: ${article.keywords}`);
|
|
73
|
+
}
|
|
74
|
+
if (article.metaKeywords) {
|
|
75
|
+
lines.push(` Meta Keywords: ${article.metaKeywords}`);
|
|
76
|
+
}
|
|
77
|
+
lines.push("");
|
|
78
|
+
lines.push(`Fields changed: ${changedFields.join(", ")}`);
|
|
79
|
+
return lines.join("\n");
|
|
80
|
+
}
|
|
61
81
|
/** Truncate text if it exceeds the character limit, with a warning. */
|
|
62
82
|
export function truncateIfNeeded(text) {
|
|
63
83
|
if (text.length <= CHARACTER_LIMIT)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatting.js","sourceRoot":"","sources":["../../src/services/formatting.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"formatting.js","sourceRoot":"","sources":["../../src/services/formatting.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session management for SupportHero write operations.
|
|
3
|
+
*
|
|
4
|
+
* SupportHero's write API requires session-based authentication via cookies
|
|
5
|
+
* (JSESSIONID and X-Session-Token), obtained by logging in through their
|
|
6
|
+
* HTML form at signin.supporthero.io.
|
|
7
|
+
*
|
|
8
|
+
* This module handles:
|
|
9
|
+
* - Logging in via the signin form
|
|
10
|
+
* - Extracting and caching session cookies across redirect hops
|
|
11
|
+
* - Making authenticated requests with automatic session renewal
|
|
12
|
+
*/
|
|
13
|
+
export declare class SessionManager {
|
|
14
|
+
private email;
|
|
15
|
+
private password;
|
|
16
|
+
private subdomain;
|
|
17
|
+
private baseUrl;
|
|
18
|
+
private cookies;
|
|
19
|
+
private isAuthenticated;
|
|
20
|
+
constructor(email: string, password: string, domain: string);
|
|
21
|
+
/**
|
|
22
|
+
* Parse Set-Cookie headers and merge into our cookie jar.
|
|
23
|
+
* Handles both single and multiple Set-Cookie values.
|
|
24
|
+
*/
|
|
25
|
+
private collectCookies;
|
|
26
|
+
/**
|
|
27
|
+
* Build a Cookie header string from our stored cookies.
|
|
28
|
+
*/
|
|
29
|
+
private getCookieHeader;
|
|
30
|
+
/**
|
|
31
|
+
* Log in to SupportHero via the signin form.
|
|
32
|
+
*
|
|
33
|
+
* Flow:
|
|
34
|
+
* 1. GET signin.supporthero.io to discover the form action and hidden fields
|
|
35
|
+
* 2. POST credentials to the form action URL
|
|
36
|
+
* 3. Manually follow redirects, collecting cookies at each hop
|
|
37
|
+
* 4. Verify we got the required session cookies
|
|
38
|
+
*/
|
|
39
|
+
login(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Make an authenticated HTTP request. Automatically logs in if needed
|
|
42
|
+
* and retries once on session expiry.
|
|
43
|
+
*/
|
|
44
|
+
authenticatedFetch(url: string, options?: RequestInit): Promise<Response>;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,eAAe,CAAS;gBAEpB,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAS3D;;;OAGG;IACH,OAAO,CAAC,cAAc;IActB;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;;;;;;;OAQG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoF5B;;;OAGG;IACG,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;CA+CpF"}
|