Skip to content

Commit 686415e

Browse files
authored
Minor fixes (#21)
1 parent 62f7ec4 commit 686415e

File tree

10 files changed

+78
-44
lines changed

10 files changed

+78
-44
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ A Model Context Protocol (MCP) server for Genesys Cloud's Platform API.
77

88
## Tools Overview
99

10-
An overview of the tools that this MPC server makes available. Read more about each specific tool
10+
An overview of the tools that this MCP server makes available. Read more about each specific tool
1111
in the [tools doc](/docs/tools.md).
1212

1313
| Tool | Description |
@@ -23,7 +23,7 @@ in the [tools doc](/docs/tools.md).
2323

2424
## Authentication
2525

26-
This currently only a supports stdio Server. To configure authentication you'll need to:
26+
This currently only supports a stdio server. To configure authentication you'll need to:
2727

2828
1. Create an OAuth Client in Genesys Cloud
2929
2. Assign the permissions to it for the tools you want to be used
@@ -43,6 +43,6 @@ npm run dev
4343
## Under active development
4444

4545
This is part of [personal project](https://www.linkedin.com/posts/lucas-woodward-the-dev_genesys-genesyscloud-vertexai-activity-7321306518908280833-cWt8?utm_source=share&utm_medium=member_desktop&rcm=ACoAABsbo2wBcmnNqxYJ5UO9BrsfURZcVEtgLOU)
46-
to create a conversational Business Insights tool. It is a practical way for my to learn MCP servers, and how best to represent Genesys Cloud's Platform APIs in a way that can be easily consumed by LLMs.
46+
to create a conversational Business Insights tool. It is a practical way for me to learn MCP servers, and how best to represent Genesys Cloud's Platform APIs in a way that can be easily consumed by LLMs.
4747

4848
There will be a lot of changes, and I will be sure to [share my learnings in my newsletter](https://makingchatbots.com/).

docs/tools.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ or listing available queues.
1515
### Inputs
1616

1717
- `name`
18-
- The name (or partial name) of the routing queue(s) to search for. Wildcards ('_') are supported for pattern matching (e.g., 'Support_', '*Emergency', '*Sales*'). Use '*' alone to retrieve all queues
18+
- The name (or partial name) of the routing queue(s) to search for. Wildcards ('\*') are supported for pattern matching (e.g., 'Support\*', '\*Emergency', '\*Sales\*'). Use '\*' alone to retrieve all queues
1919
- `pageNumber`
2020
- The page number of the results to retrieve, starting from 1. Defaults to 1 if not specified. Used with 'pageSize' for navigating large result sets
2121
- `pageSize`
@@ -180,7 +180,7 @@ Searches for voice conversations within a specified time window, optionally filt
180180
- `pageNumber`
181181
- The page number of the results to retrieve, starting from 1. Defaults to 1 if not specified. Used with 'pageSize' for navigating large result sets
182182
- `pageSize`
183-
- The maximum number of conversations to return per page. Defaults to 100 if not specified. Used with 'pageNumber' for pagination. The maximum value is 500
183+
- The maximum number of conversations to return per page. Defaults to 100 if not specified. Used with 'pageNumber' for pagination. The maximum value is 100
184184
- `startDate`
185185
- The start date/time in ISO-8601 format (e.g., '2024-01-01T00:00:00Z')
186186
- `endDate`

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@makingchatbots/genesys-cloud-mcp-server",
3-
"version": "0.0.9",
3+
"version": "0.0.10",
44
"description": "A Model Context Protocol (MCP) server exposing Genesys Cloud tools for LLMs, including sentiment analysis, conversation search, topic detection and more.",
55
"exports": "./dist/index.js",
66
"type": "module",

src/tools/conversationTranscription/conversationTranscription.ts

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -75,36 +75,27 @@ function isNonHuman(participant: Participant | undefined) {
7575
);
7676
}
7777

78-
function isInternalParticipant(participant: Participant) {
79-
if (participant.participantPurpose) {
80-
return (
81-
participant.participantPurpose.toLowerCase() === "user" ||
82-
participant.participantPurpose.toLowerCase() === "agent" ||
83-
isNonHuman(participant)
84-
);
85-
} else if (
86-
participant.participantPurpose &&
87-
participant.participantPurpose.toLowerCase() === "internal"
88-
) {
89-
return !!participant.userId;
78+
function isInternalParticipant(participant: Participant): boolean {
79+
const purpose = participant.participantPurpose?.toLowerCase();
80+
if (!purpose) {
81+
return false;
9082
}
91-
return false;
83+
84+
return (
85+
purpose === "user" ||
86+
purpose === "agent" ||
87+
purpose === "internal" ||
88+
isNonHuman(participant)
89+
);
9290
}
9391

94-
function isExternalParticipant(participant: Participant) {
95-
if (participant.participantPurpose) {
96-
return (
97-
participant.participantPurpose.toLowerCase() === "external" ||
98-
participant.participantPurpose.toLowerCase() === "customer" ||
99-
!isNonHuman(participant)
100-
);
101-
} else if (
102-
participant.participantPurpose &&
103-
participant.participantPurpose.toLowerCase() === "external"
104-
) {
105-
return true;
92+
function isExternalParticipant(participant: Participant): boolean {
93+
const purpose = participant.participantPurpose?.toLowerCase();
94+
if (!purpose) {
95+
return false;
10696
}
107-
return false;
97+
98+
return purpose === "external" || purpose === "customer";
10899
}
109100

110101
const paramsSchema = z.object({
@@ -164,16 +155,16 @@ export const conversationTranscription: ToolFactory<
164155
// 2. Download recordings
165156
const transcriptionsForRecordings: TranscriptResponseFormat[] = [];
166157

167-
for (const recodingSessionId of recordingSessionIds) {
168-
if (!recodingSessionId) {
158+
for (const recordingSessionId of recordingSessionIds) {
159+
if (!recordingSessionId) {
169160
continue;
170161
}
171162
let transcriptUrl: Models.TranscriptUrl | null = null;
172163
try {
173164
transcriptUrl =
174165
await speechTextAnalyticsApi.getSpeechandtextanalyticsConversationCommunicationTranscripturl(
175166
conversationId,
176-
recodingSessionId,
167+
recordingSessionId,
177168
);
178169
} catch (error) {
179170
const message = isUnauthorisedError(error)

src/tools/sampleConversationsByQueue/sampleConversationsByQueue.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@ export const sampleConversationsByQueue: ToolFactory<
9999
await analyticsApi.getAnalyticsConversationsDetailsJob(jobId);
100100
state = jobStatus.state ?? "UNKNOWN";
101101

102-
console.log("UPDATE", { attempts, state });
103-
104102
if (state === "FULFILLED") break;
105103

106104
switch (jobStatus.state) {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { describe, expect, test } from "vitest";
2+
import { sampleEvenly } from "./sampleEvenly.js";
3+
4+
describe("sampleEvenly", () => {
5+
test("returns evenly distributed items when sample size is less than list length", () => {
6+
const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
7+
const result = sampleEvenly(list, 4);
8+
expect(result).toStrictEqual([1, 3, 6, 8]);
9+
});
10+
11+
test("returns original list when sample size is greater than or equal to list length", () => {
12+
const list = [1, 2, 3, 4];
13+
expect(sampleEvenly(list, 4)).toStrictEqual(list);
14+
expect(sampleEvenly(list, 5)).toStrictEqual(list);
15+
});
16+
});

src/tools/searchQueues.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function formatQueues(
2323
pageNumber?: number;
2424
pageSize?: number;
2525
pageCount?: number;
26-
total?: number;
26+
totalHits?: number;
2727
},
2828
) {
2929
const queueItems = queues.flatMap((q) => [
@@ -35,8 +35,13 @@ function formatQueues(
3535
: []),
3636
]);
3737

38+
const firstLine =
39+
pagination.totalHits !== undefined
40+
? `Found ${String(pagination.totalHits)} queues matching "${inputQueueName}":`
41+
: `Found the following queues matching "${inputQueueName}":`;
42+
3843
return [
39-
`Found the following queues matching "${inputQueueName}":`,
44+
firstLine,
4045
...queueItems,
4146
"",
4247
...paginationSection("Total Matching Queues", pagination),
@@ -129,7 +134,7 @@ export const searchQueues: ToolFactory<
129134
pageNumber: result.pageNumber,
130135
pageSize: result.pageSize,
131136
pageCount: result.pageCount,
132-
total: result.total,
137+
totalHits: result.total,
133138
}),
134139
},
135140
],

src/tools/searchVoiceConversations.test.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ describe("Search Voice Conversations Tool", () => {
6363
},
6464
pageSize: {
6565
description:
66-
"The maximum number of conversations to return per page. Defaults to 100 if not specified. Used with 'pageNumber' for pagination. The maximum value is 500",
66+
"The maximum number of conversations to return per page. Defaults to 100 if not specified. Used with 'pageNumber' for pagination. The maximum value is 100",
6767
exclusiveMinimum: 0,
6868
maximum: 100,
6969
type: "integer",
@@ -130,6 +130,30 @@ describe("Search Voice Conversations Tool", () => {
130130
});
131131
});
132132

133+
test("fails when startDate is after endDate", async () => {
134+
toolDeps.analyticsApi.postAnalyticsConversationsDetailsQuery.mockResolvedValue(
135+
{ conversations: [] },
136+
);
137+
138+
await expect(
139+
client.callTool({
140+
name: toolName,
141+
arguments: {
142+
startDate: "2024-01-02T00:00:00Z",
143+
endDate: "2024-01-01T00:00:00Z",
144+
},
145+
}),
146+
).resolves.toStrictEqual({
147+
isError: true,
148+
content: [
149+
{
150+
type: "text",
151+
text: "Start date must be before end date.",
152+
},
153+
],
154+
});
155+
});
156+
133157
test("error from Genesys Cloud's Platform SDK returned", async () => {
134158
toolDeps.analyticsApi.postAnalyticsConversationsDetailsQuery.mockRejectedValue(
135159
new Error("Test Error Message"),

src/tools/searchVoiceConversations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const paramsSchema = z.object({
5656
.max(100)
5757
.optional()
5858
.describe(
59-
"The maximum number of conversations to return per page. Defaults to 100 if not specified. Used with 'pageNumber' for pagination. The maximum value is 500",
59+
"The maximum number of conversations to return per page. Defaults to 100 if not specified. Used with 'pageNumber' for pagination. The maximum value is 100",
6060
),
6161
startDate: z
6262
.string()

src/tools/utils/paginationSection.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const testCases: {
2222
],
2323
},
2424
{
25-
name: "",
25+
name: "Missing total hits",
2626
input: {
2727
pageSize: 100,
2828
pageNumber: 1,
@@ -36,7 +36,7 @@ const testCases: {
3636
],
3737
},
3838
{
39-
name: "",
39+
name: "Divisible hit count",
4040
input: {
4141
pageSize: 100,
4242
pageNumber: 1,

0 commit comments

Comments
 (0)