Skip to content

Commit 76b0a62

Browse files
committed
Tweaks to Q Dev CLI-generated code
1 parent 1c83aed commit 76b0a62

File tree

14 files changed

+280
-869
lines changed

14 files changed

+280
-869
lines changed

examples/chatbot-typescript/package-lock.json

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

examples/chatbot-typescript/package.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
{
22
"name": "mcp-chatbot-typescript",
3-
"version": "1.0.0",
4-
"main": "dist/main.js",
3+
"version": "0.1.0",
4+
"main": "build/main.js",
5+
"types": "build/main.d.ts",
56
"scripts": {
67
"build": "tsc",
7-
"start": "ts-node src/main.ts",
8+
"start": "node build/main.js",
89
"lint": "eslint . --ext .ts"
910
},
1011
"dependencies": {
1112
"@aws-sdk/client-bedrock-runtime": "^3.750.0",
1213
"@modelcontextprotocol/sdk": "^1.5.0",
13-
"mcp-lambda": "file:../../src/typescript",
1414
"readline-sync": "^1.4.10",
1515
"winston": "^3.17.0"
1616
},
@@ -19,7 +19,6 @@
1919
"@types/node": "^22.13.5",
2020
"@types/readline-sync": "^1.4.8",
2121
"eslint": "^9.8.0",
22-
"tsx": "^4.16.5",
2322
"typescript": "^5.7.3",
2423
"typescript-eslint": "^8.0.0"
2524
}

examples/chatbot-typescript/src/chat_session.ts

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
import * as readline from 'readline-sync';
2-
import { Server } from './server_clients/server.js';
3-
import { Servers } from './server_clients/servers.js';
4-
import { LLMClient } from './llm_client.js';
5-
import logger from './logger.js';
1+
import * as readline from "readline-sync";
2+
import { Server } from "./server_clients/server.js";
3+
import { Servers } from "./server_clients/servers.js";
4+
import { LLMClient } from "./llm_client.js";
5+
import logger from "./logger.js";
6+
import {
7+
ContentBlock,
8+
ConverseCommandOutput,
9+
Message,
10+
} from "@aws-sdk/client-bedrock-runtime";
611

712
/**
813
* Orchestrates the interaction between user, LLM, and tools.
@@ -24,26 +29,28 @@ export class ChatSession {
2429
*/
2530
async executeRequestedTools(
2631
serversManager: Servers,
27-
llmResponse: Record<string, any>
28-
): Promise<Record<string, any> | null> {
32+
llmResponse: ConverseCommandOutput
33+
): Promise<Message | null> {
2934
const stopReason = llmResponse.stopReason;
3035

31-
if (stopReason === 'tool_use') {
36+
if (stopReason === "tool_use") {
3237
try {
33-
const toolResponses = [];
34-
for (const contentItem of llmResponse.output.message.content) {
35-
if ('toolUse' in contentItem) {
36-
logger.info(`Executing tool: ${contentItem.toolUse.name}`);
37-
logger.info(`With arguments: ${JSON.stringify(contentItem.toolUse.input)}`);
38+
const toolResponses: ContentBlock[] = [];
39+
for (const contentItem of llmResponse.output!.message!.content!) {
40+
if ("toolUse" in contentItem) {
41+
logger.info(`Executing tool: ${contentItem.toolUse!.name}`);
42+
logger.info(
43+
`With arguments: ${JSON.stringify(contentItem.toolUse!.input)}`
44+
);
3845
const response = await serversManager.executeTool(
39-
contentItem.toolUse.name,
40-
contentItem.toolUse.toolUseId,
41-
contentItem.toolUse.input
46+
contentItem.toolUse!.name!,
47+
contentItem.toolUse!.toolUseId!,
48+
contentItem.toolUse!.input! as Record<string, any>
4249
);
4350
toolResponses.push(response);
4451
}
4552
}
46-
return { role: 'user', content: toolResponses };
53+
return { role: "user", content: toolResponses };
4754
} catch (e) {
4855
throw new Error(`Failed to execute tool: ${e}`);
4956
}
@@ -58,50 +65,61 @@ export class ChatSession {
5865
*/
5966
async start(): Promise<void> {
6067
const serverManager = new Servers(this.servers);
61-
68+
6269
try {
6370
await serverManager.initialize();
64-
71+
6572
const allTools = await serverManager.listTools();
66-
const toolsDescription = allTools.map(tool => tool.formatForLLM());
73+
const toolsDescription = allTools.map((tool) => tool.formatForLLM());
6774

68-
const systemPrompt = 'You are a helpful assistant.';
75+
const systemPrompt = "You are a helpful assistant.";
6976

70-
const messages: Record<string, any>[] = [];
77+
const messages: Message[] = [];
7178

7279
while (true) {
7380
try {
74-
const userInput = readline.question('\nYou: ').trim().toLowerCase();
75-
if (userInput === 'quit' || userInput === 'exit') {
76-
logger.info('\nExiting...');
81+
const userInput = readline.question("\nYou: ").trim().toLowerCase();
82+
if (userInput === "quit" || userInput === "exit") {
83+
logger.info("\nExiting...");
7784
break;
7885
}
7986

80-
messages.push({ role: 'user', content: [{ text: userInput }] });
87+
messages.push({ role: "user", content: [{ text: userInput }] });
8188

8289
const llmResponse = await this.llmClient.getResponse(
8390
messages,
8491
systemPrompt,
8592
toolsDescription
8693
);
87-
88-
logger.debug('\nAssistant: ' + JSON.stringify(llmResponse, null, 2));
89-
console.log(`\nAssistant: ${llmResponse.output.message.content[0].text}`);
90-
messages.push(llmResponse.output.message);
9194

92-
const toolResults = await this.executeRequestedTools(serverManager, llmResponse);
95+
logger.debug("\nAssistant: " + JSON.stringify(llmResponse, null, 2));
96+
console.log(
97+
`\nAssistant: ${llmResponse.output!.message!.content![0].text}`
98+
);
99+
messages.push(llmResponse.output!.message!);
100+
101+
const toolResults = await this.executeRequestedTools(
102+
serverManager,
103+
llmResponse
104+
);
93105

94106
if (toolResults) {
95-
logger.debug('\nTool Results: ' + JSON.stringify(toolResults, null, 2));
107+
logger.debug(
108+
"\nTool Results: " + JSON.stringify(toolResults, null, 2)
109+
);
96110
messages.push(toolResults);
97111
const finalResponse = await this.llmClient.getResponse(
98112
messages,
99113
systemPrompt,
100114
toolsDescription
101115
);
102-
logger.debug('\nFinal response: ' + JSON.stringify(finalResponse, null, 2));
103-
console.log(`\nAssistant: ${finalResponse.output.message.content[0].text}`);
104-
messages.push(finalResponse.output.message);
116+
logger.debug(
117+
"\nFinal response: " + JSON.stringify(finalResponse, null, 2)
118+
);
119+
console.log(
120+
`\nAssistant: ${finalResponse.output!.message!.content![0].text}`
121+
);
122+
messages.push(finalResponse.output!.message!);
105123
}
106124
} catch (e) {
107125
logger.error(`Error: ${e}`);

examples/chatbot-typescript/src/llm_client.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { BedrockRuntimeClient, ConverseCommand } from '@aws-sdk/client-bedrock-runtime';
1+
import {
2+
BedrockRuntimeClient,
3+
ConverseCommand,
4+
ConverseCommandOutput,
5+
Message,
6+
Tool,
7+
} from "@aws-sdk/client-bedrock-runtime";
28

39
/**
410
* Manages communication with Bedrock.
@@ -14,16 +20,16 @@ export class LLMClient {
1420

1521
/**
1622
* Get a response from the LLM, using the Bedrock Converse API.
17-
* @param messages A list of message dictionaries.
23+
* @param messages A list of message for the model.
1824
* @param systemPrompt The system prompt to use.
1925
* @param tools The list of tools to make available to the model.
2026
* @returns The LLM's full response.
2127
*/
2228
async getResponse(
23-
messages: Record<string, any>[],
29+
messages: Message[],
2430
systemPrompt: string,
25-
tools: Record<string, any>[]
26-
): Promise<Record<string, any>> {
31+
tools: Tool[]
32+
): Promise<ConverseCommandOutput> {
2733
const command = new ConverseCommand({
2834
modelId: this.modelId,
2935
messages,
@@ -36,7 +42,6 @@ export class LLMClient {
3642
toolConfig: { tools },
3743
});
3844

39-
const response = await this.bedrockClient.send(command);
40-
return response as Record<string, any>;
45+
return await this.bedrockClient.send(command);
4146
}
4247
}

examples/chatbot-typescript/src/logger.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,4 @@ const logger = winston.createLogger({
2222
],
2323
});
2424

25-
// Silence AWS SDK logs unless explicitly enabled
26-
if (process.env.LOG_LEVEL !== 'debug') {
27-
logger.silent = false;
28-
}
29-
3025
export default logger;
Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,48 @@
1-
import { Configuration } from './configuration.js';
2-
import { ChatSession } from './chat_session.js';
3-
import { LLMClient } from './llm_client.js';
4-
import { StdioServer } from './server_clients/stdio_server.js';
5-
import { LambdaFunctionClient } from './server_clients/lambda_function.js';
6-
import { Server } from './server_clients/server.js';
7-
import logger from './logger.js';
1+
import { Configuration } from "./configuration.js";
2+
import { ChatSession } from "./chat_session.js";
3+
import { LLMClient } from "./llm_client.js";
4+
import { StdioServer } from "./server_clients/stdio_server.js";
5+
import { LambdaFunctionClient } from "./server_clients/lambda_function.js";
6+
import { Server } from "./server_clients/server.js";
7+
import logger from "./logger.js";
88

99
/**
1010
* Initialize and run the chat session.
1111
*/
1212
async function main(): Promise<void> {
13-
// Set log level based on environment variable
14-
if (process.env.DEBUG) {
15-
logger.level = 'debug';
16-
}
17-
1813
const config = new Configuration();
19-
const serverConfig = Configuration.loadConfig('./servers_config.json');
20-
14+
const serverConfig = Configuration.loadConfig("./servers_config.json");
15+
2116
const servers: Server[] = [];
22-
17+
2318
// Initialize stdio servers
2419
for (const [name, srvConfig] of Object.entries(serverConfig.stdioServers)) {
2520
servers.push(new StdioServer(name, srvConfig as Record<string, any>));
2621
}
27-
22+
2823
// Initialize Lambda function servers
29-
for (const [name, srvConfig] of Object.entries(serverConfig.lambdaFunctionServers)) {
30-
servers.push(new LambdaFunctionClient(name, srvConfig as Record<string, any>));
24+
for (const [name, srvConfig] of Object.entries(
25+
serverConfig.lambdaFunctionServers
26+
)) {
27+
servers.push(
28+
new LambdaFunctionClient(name, srvConfig as Record<string, any>)
29+
);
3130
}
32-
31+
3332
const llmClient = new LLMClient(config.bedrockClient, config.modelId);
3433
const chatSession = new ChatSession(servers, llmClient);
35-
34+
3635
await chatSession.start();
3736
}
3837

3938
// Handle errors
40-
process.on('unhandledRejection', (reason, promise) => {
41-
logger.error('Unhandled Rejection at:', promise, 'reason:', reason);
39+
process.on("unhandledRejection", (reason, promise) => {
40+
logger.error("Unhandled Rejection at:", promise, "reason:", reason);
4241
process.exit(1);
4342
});
4443

4544
// Run the main function
46-
main().catch(error => {
47-
logger.error('Error in main:', error);
45+
main().catch((error) => {
46+
logger.error("Error in main:", error);
4847
process.exit(1);
4948
});

examples/chatbot-typescript/src/server_clients/lambda_function.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { ClientSession } from '@modelcontextprotocol/sdk';
2-
import { LambdaFunctionParameters, lambdaFunctionClient } from 'mcp-lambda';
3-
import { Server } from './server.js';
4-
import logger from '../logger.js';
1+
import {
2+
LambdaFunctionParameters,
3+
LambdaFunctionClientTransport,
4+
} from "mcp-lambda";
5+
import { Server } from "./server.js";
6+
import logger from "../logger.js";
57

68
/**
79
* Manages MCP server connections and tool execution for servers running in Lambda functions.
@@ -13,25 +15,23 @@ export class LambdaFunctionClient extends Server {
1315
* @throws RuntimeError if server fails to initialize
1416
*/
1517
async initialize(): Promise<void> {
16-
const functionName = this.config.functionName;
17-
if (!functionName) {
18-
throw new Error('The functionName must be a valid string and cannot be None.');
18+
if (!this.config.functionName) {
19+
throw new Error(
20+
"The functionName must be a valid string and cannot be None."
21+
);
1922
}
20-
21-
const regionName = this.config.region;
2223

2324
const serverParams: LambdaFunctionParameters = {
24-
functionName,
25-
regionName,
25+
functionName: this.config.functionName,
26+
regionName: this.config.region,
2627
};
2728

29+
const transport = new LambdaFunctionClientTransport(serverParams);
30+
2831
try {
29-
this._client = lambdaFunctionClient(serverParams);
30-
const { read, write } = await this._client.open();
31-
this.session = new ClientSession(read, write);
32-
await this.session.initialize();
32+
await this.client.connect(transport);
3333
} catch (e) {
34-
logger.error(`Error initializing Lambda function client ${this.name}: ${e}`);
34+
logger.error(`Error initializing server ${this.name}: ${e}`);
3535
throw e;
3636
}
3737
}

0 commit comments

Comments
 (0)