Skip to content

Commit ead1a9d

Browse files
authored
Add cache and tweaks (#44)
1 parent a9e4d17 commit ead1a9d

File tree

7 files changed

+105
-39
lines changed

7 files changed

+105
-39
lines changed

README.md

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ A Model Context Protocol (MCP) server for Genesys Cloud's Platform API.
2222

2323
## Usage with Claude Desktop
2424

25+
### MCP Bundle
26+
27+
This MCP Server provides an [MCP Bundle](https://github.com/anthropics/mcpb) (.mcpb file) along with each [release](https://github.com/MakingChatbots/genesys-cloud-mcp-server/releases),
28+
which is a single-click installable package for Claude Desktop. To use it:
29+
30+
1. Download the `.mcpb` file from the [latest release](https://github.com/MakingChatbots/genesys-cloud-mcp-server/releases)
31+
2. In Claude Desktop navigate to Settings > Extensions.
32+
3. Open the .mcpb file with Claude
33+
4. Configure the Region and OAuth Client for the extension
34+
35+
The extension will now be available in your conversations.
36+
2537
### NPX
2638

2739
Add this to your `claude_desktop_config.json`:
@@ -43,19 +55,6 @@ Add this to your `claude_desktop_config.json`:
4355
}
4456
```
4557

46-
### MCP Bundle
47-
48-
This MCP Server provides an [MCP Bundle](https://github.com/anthropics/mcpb) (.mcpb file) along with each [release](https://github.com/MakingChatbots/genesys-cloud-mcp-server/releases),
49-
which is a single-click installable package for Claude Desktop. To use it:
50-
51-
1. Download the `.mcpb` file from the [latest release](https://github.com/MakingChatbots/genesys-cloud-mcp-server/releases)
52-
2. In Claude Desktop navigate to Settings > Extensions.
53-
3. Browse to, or drag in the .mcpb file downloaded
54-
4. Click "Install"
55-
5. Configure the Region and OAuth Client for the extension
56-
57-
The extension will now be available in your conversations.
58-
5958
## Usage with Gemini CLI
6059

6160
Add below to your `.gemini/settings.json` file. You can read more about the [setup from the official guide](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/tutorials.md#configure-the-mcp-server-in-settingsjson).

manifest.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": "0.2",
33
"name": "Genesys Cloud MCP Server",
4-
"version": "0.0.16",
4+
"version": "1.0.2",
55
"description": "Interact with Genesys Cloud's Platform API",
66
"long_description": "This extension allows Claude to connect to Genesys Cloud's Platform API via a local MCP server. It provides tools for querying queue volumes, retrieving conversation samples, analyzing sentiment and voice quality, accessing transcripts, and more.\n\nThis project is not affiliated with Genesys.",
77
"author": {
@@ -55,6 +55,14 @@
5555
{
5656
"name": "conversation_transcript",
5757
"description": "Retrieves a structured transcript of the conversation, including speaker labels, utterance timestamps, and sentiment annotations where available. The transcript is formatted as a time-aligned list of utterances attributed to each participant (e.g., customer or agent)."
58+
},
59+
{
60+
"name": "oauth_clients",
61+
"description": "Retrieves a list of all OAuth clients, including their associated roles and divisions. This tool is useful for auditing and managing OAuth clients in the Genesys Cloud organization."
62+
},
63+
{
64+
"name": "oauth_client_usage",
65+
"description": "Retrieves the usage of an OAuth Client for a given period. It returns the total number of requests and a breakdown of requests per organization."
5866
}
5967
],
6068
"user_config": {

package-lock.json

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@makingchatbots/genesys-cloud-mcp-server",
3-
"version": "1.0.1",
3+
"version": "1.0.2",
44
"description": "A Model Context Protocol (MCP) server exposing Genesys Cloud tools for LLMs, including sentiment analysis, conversation search, topic detection and more.",
55
"bin": "./dist/cli.js",
66
"type": "module",
@@ -39,19 +39,20 @@
3939
"dependencies": {
4040
"@modelcontextprotocol/sdk": "^1.18.2",
4141
"date-fns": "^4.1.0",
42+
"lru-cache": "^11.2.4",
4243
"purecloud-platform-client-v2": "^239.0.0",
4344
"zod": "^3.23.8"
4445
},
4546
"devDependencies": {
46-
"dotenv": "^17.2.2",
4747
"@anthropic-ai/mcpb": "^2.0.1",
48+
"@biomejs/biome": "2.2.4",
4849
"@google/genai": "^1.12.0",
4950
"@types/node": "^18.19.130",
51+
"dotenv": "^17.2.2",
5052
"lint-staged": "^16.2.7",
5153
"tsx": "^4.20.6",
5254
"typescript": "^5.8.3",
53-
"vitest": "^3.2.4",
54-
"@biomejs/biome": "2.2.4"
55+
"vitest": "^3.2.4"
5556
},
5657
"lint-staged": {
5758
"src/*.ts": [

src/index.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3+
import { LRUCache } from "lru-cache";
34
import platformClient from "purecloud-platform-client-v2";
45
import { OAuthClientCredentialsWrapper } from "./auth/OAuthClientCredentialsWrapper.js";
56
import { createConfigRetriever } from "./createConfigRetriever.js";
67
import { conversationSentiment } from "./tools/conversationSentiment/conversationSentiment.js";
78
import { conversationTopics } from "./tools/conversationTopics/conversationTopics.js";
89
import { conversationTranscription } from "./tools/conversationTranscription/conversationTranscription.js";
910
import { oauthClients } from "./tools/oauthClients/oauthClients.js";
10-
import { oauthClientUsage } from "./tools/oauthClientUsage/oauthClientUsage.js";
11+
import {
12+
type OAuthClientUsageResponse,
13+
oauthClientUsage,
14+
} from "./tools/oauthClientUsage/oauthClientUsage.js";
1115
import { queryQueueVolumes } from "./tools/queryQueueVolumes/queryQueueVolumes.js";
1216
import { sampleConversationsByQueue } from "./tools/sampleConversationsByQueue/sampleConversationsByQueue.js";
1317
import { searchQueues } from "./tools/searchQueues.js";
@@ -21,7 +25,17 @@ const withAuth = OAuthClientCredentialsWrapper(
2125

2226
const server: McpServer = new McpServer({
2327
name: "Genesys Cloud",
24-
version: "1.0.1", // Same version as version in package.json
28+
version: "1.0.2", // Same version as version in package.json
29+
});
30+
31+
const cache = new LRUCache<string, OAuthClientUsageResponse>({
32+
max: 500,
33+
ttl: 1000 * 60 * 5, // 5 minutes
34+
35+
allowStale: false,
36+
37+
updateAgeOnGet: false,
38+
updateAgeOnHas: false,
2539
});
2640

2741
const routingApi = new platformClient.RoutingApi();
@@ -147,6 +161,7 @@ server.registerTool(
147161
);
148162

149163
const oauthClientUsageTool = oauthClientUsage({
164+
cache,
150165
oauthApi,
151166
});
152167
server.registerTool(

src/tools/oauthClientUsage/oauthClientUsage.ts

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { LRUCache } from "lru-cache";
12
import type { Models, OAuthApi } from "purecloud-platform-client-v2";
23
import { z } from "zod";
34
import { createTool, type ToolFactory } from "../utils/createTool.js";
@@ -6,12 +7,24 @@ import { isUnauthorisedError } from "../utils/genesys/isUnauthorisedError.js";
67
import { waitFor } from "../utils/waitFor.js";
78

89
const MAX_ATTEMPTS = 10;
10+
const TOOL_CACHE_KEY = "oauthClientUsage";
11+
12+
export interface OAuthClientUsageResponse {
13+
startDate: string;
14+
endDate: string;
15+
totalRequest: number;
16+
requestsPerOrganisation: {
17+
organisationId?: string;
18+
requests?: number;
19+
}[];
20+
}
921

1022
export interface ToolDependencies {
1123
readonly oauthApi: Pick<
1224
OAuthApi,
1325
"postOauthClientUsageQuery" | "getOauthClientUsageQueryResult"
1426
>;
27+
readonly cache?: LRUCache<string, OAuthClientUsageResponse>;
1528
}
1629

1730
const paramsSchema = z.object({
@@ -36,7 +49,7 @@ const paramsSchema = z.object({
3649
export const oauthClientUsage: ToolFactory<
3750
ToolDependencies,
3851
typeof paramsSchema
39-
> = ({ oauthApi }) =>
52+
> = ({ oauthApi, cache }) =>
4053
createTool({
4154
schema: {
4255
name: "oauth_client_usage",
@@ -59,14 +72,27 @@ export const oauthClientUsage: ToolFactory<
5972
to.setTime(now.getTime());
6073
}
6174

75+
const cacheName = `${TOOL_CACHE_KEY}.${oauthClientId}-${from.getTime()}-${to.getTime()}`;
76+
77+
const cachedResult = cache?.get(cacheName);
78+
if (cachedResult) {
79+
return {
80+
content: [
81+
{
82+
type: "text",
83+
text: JSON.stringify(cachedResult),
84+
},
85+
],
86+
};
87+
}
88+
6289
let result: Models.UsageExecutionResult;
6390
try {
6491
result = await oauthApi.postOauthClientUsageQuery(oauthClientId, {
6592
interval: `${from.toISOString()}/${to.toISOString()}`,
6693
metrics: ["Requests"],
6794
groupBy: ["OrganizationId"],
6895
});
69-
console.log(result);
7096
} catch (error: unknown) {
7197
const errorMessage = isUnauthorisedError(error)
7298
? "Failed to retrieve usage of OAuth client: Unauthorised access. Please check API credentials or permissions"
@@ -118,24 +144,31 @@ export const oauthClientUsage: ToolFactory<
118144
);
119145
}
120146

147+
const toolResult: OAuthClientUsageResponse = {
148+
startDate,
149+
endDate,
150+
totalRequest: (apiUsageQueryResult?.results ?? []).reduce(
151+
(acc, curr) => acc + (curr.requests ?? 0),
152+
0,
153+
),
154+
requestsPerOrganisation: (apiUsageQueryResult?.results ?? []).map(
155+
(result) => ({
156+
organisationId: result.organizationId,
157+
requests: result.requests,
158+
}),
159+
),
160+
};
161+
162+
cache?.set(
163+
`${TOOL_CACHE_KEY}.${oauthClientId}-${from.getTime()}-${to.getTime()}`,
164+
toolResult,
165+
);
166+
121167
return {
122168
content: [
123169
{
124170
type: "text",
125-
text: JSON.stringify({
126-
startDate,
127-
endDate,
128-
totalRequest: (apiUsageQueryResult?.results ?? []).reduce(
129-
(acc, curr) => acc + (curr.requests ?? 0),
130-
0,
131-
),
132-
requestsPerOrganisation: (apiUsageQueryResult?.results ?? []).map(
133-
(result) => ({
134-
organisationId: result.organizationId,
135-
requests: result.requests,
136-
}),
137-
),
138-
}),
171+
text: JSON.stringify(toolResult),
139172
},
140173
],
141174
};

tests/integration/serverRuns.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ describe("Server Runs", () => {
8181

8282
client = new Client({
8383
name: "test-client",
84-
version: "1.0.1",
84+
version: "1.0.2",
8585
});
8686

8787
await client.connect(transport);

0 commit comments

Comments
 (0)