totoms 2.3.2 → 2.3.3

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 CHANGED
@@ -1,555 +1,702 @@
1
- # Toto Microservice SDK - NodeJS
2
-
3
- The Toto Microservice SDK is a framework for building cloud-agnostic microservices. <br>
4
- This is the NodeJS SDK documentation.
5
-
6
- ## Table of Contents
7
-
8
- 1. [Installation](#1-installation)
9
- 2. [Overview](#2-overview)
10
- 3. [Usage](#3-usage)
11
- - [3.1. The Toto Microservice Configuration](#31-the-toto-microservice-configuration)
12
- - [3.2. Create and Register APIs](#32-create-and-register-apis)
13
- * [Exposing the OpenAPI Spec through Swagger UI](#exposing-the-openapi-spec-through-swagger-ui)
14
- - [3.3. Use a Message Bus](#33-use-a-message-bus)
15
- - [3.4. Expose MCP Tools](#34-expose-mcp-tools)
16
- - [3.5. Load Secrets](#35-load-secrets)
17
- - [3.6. Custom Configurations](#36-custom-configurations)
18
-
19
- Other:
20
- * [Build and Deploy on NPM](./docs/buildpublish.md)
21
-
22
- ## 1. Installation
23
-
24
- ```bash
25
- npm install totoms
26
- ```
27
-
28
- ### Cloud-Specific Dependencies
29
-
30
- Install the peer dependencies for your target cloud platform:
31
-
32
- **AWS:**
33
- ```bash
34
- npm install @aws-sdk/client-secrets-manager @aws-sdk/client-sns @aws-sdk/client-sqs
35
- ```
36
-
37
- **GCP:**
38
- ```bash
39
- npm install @google-cloud/pubsub @google-cloud/secret-manager
40
- ```
41
-
42
- ## 2. Overview
43
-
44
- Everything starts with `TotoMicroservice` and the `TotoMicroserviceConfiguration`.<br>
45
- `TotoMicroservice` is the main orchestrator that coordinates your entire microservice. It initializes and manages:
46
-
47
- - **API Controller & API Endpoints**: Express-based REST API setup with automatic endpoint registration
48
- - **Message Bus & Message Handlers**: Event-driven communication via Pub/Sub and Queues. Registration and routing of event handlers to appropriate topics.
49
- - **Secrets Management**: Automatic loading of secrets from your cloud provider
50
- - **Service Lifecycle**: Initialization, startup, and shutdown management
51
-
52
- The configuration is **declarative**. The goal is to make it very simple to configure a full microservice, with a syntax that will look like this:
53
-
54
- ```typescript
55
- import { getHyperscalerConfiguration, SupportedHyperscalers, TotoMicroservice, TotoMicroserviceConfiguration } from 'totoms';
56
- import { ControllerConfig } from "./Config";
57
- import { SayHello } from './dlg/ExampleDelegate';
58
-
59
-
60
- const config: TotoMicroserviceConfiguration = {
61
- serviceName: "toto-ms-ex1",
62
- basePath: '/ex1',
63
- environment: {
64
- hyperscaler: process.env.HYPERSCALER as SupportedHyperscalers || "aws",
65
- hyperscalerConfiguration: getHyperscalerConfiguration()
66
- },
67
- customConfiguration: ControllerConfig,
68
- apiConfiguration: {
69
- apiEndpoints: [
70
- { method: 'GET', path: '/hello', delegate: SayHello }
71
- ],
72
- apiOptions: { noCorrelationId: true }
73
- },
74
- };
75
-
76
- TotoMicroservice.init(config).then(microservice => {
77
- microservice.start();
78
- });
79
- ```
80
-
81
- A **few things you should pay attention to**:
82
- * `ControllerConfig` - that's your custom configuration class, that you can use to do any type of custom initialization and work (e.g. loading secrets). <br>
83
- You can find [more details in this section](#31-the-toto-microservice-configuration)
84
-
85
- The `TotoMicroserviceConfiguration` object specifies:
86
-
87
- - **Service Metadata**: Service name and base path for API endpoints
88
- - **Environment**: Cloud provider (AWS, GCP, Azure) information
89
- - **API Configuration**: REST endpoints with their handlers
90
- - **Message Bus Configuration**: Topics to subscribe to and message handlers
91
- - **Custom Configuration**: Your application-specific settings
92
-
93
- ## 3. Usage
94
-
95
- ### 3.1. The Toto Microservice Configuration
96
-
97
- The microservice is configured through the `TotoMicroserviceConfiguration` object and the `TotoControllerConfig` base class. <br>
98
- As seen above, you need to define a **Custom Configuration Class** that extends the `TotoControllerConfig` base class as shown below here.
99
-
100
- ```typescript
101
- import { TotoControllerConfig } from 'totoms';
102
-
103
- export class ControllerConfig extends TotoControllerConfig {
104
-
105
- getMongoSecretNames(): { userSecretName: string; pwdSecretName: string; } | null {
106
- return null;
107
- }
108
-
109
- getProps(): APIOptions {
110
- return {}
111
- }
112
-
113
- }
114
- ```
115
-
116
- Some things to **note**:
117
- * The `getMongoSecretNames()` method allows you to define the name of the Secrets containing user and pswd of your Mongo DB, if you choose to use it (stored in the Cloud Secrets Manager, depending on the cloud you're deploying to).
118
- * The `getProps()` method allows you to do some overrides (e.g. no authentication for this service). You can explore the properties, they're well documented in the SDK.
119
-
120
- ### 3.2. Create and Register APIs
121
-
122
- Your microservice exposes REST API endpoints using Express. <br>
123
- Endpoints are defined when creating the API controller and are automatically set up.
124
-
125
- #### Create a Toto Delegate
126
-
127
- Every endpoint needs to be managed by a **Toto Delegate**. <br>
128
- Toto Delegates extend the `TotoDelegate` abstract class.
129
-
130
- This is how you define a Toto Delegate. <br>
131
- *The following example shows a delegate that processes user creation*.
132
-
133
- ```typescript
134
- import { TotoDelegate, UserContext, ValidationError, TotoRequest } from 'totoms';
135
- import { Request } from 'express';
136
-
137
- class CreateUserDelegate extends TotoDelegate<CreateUserRequest, CreateUserResponse> {
138
-
139
- async do(req: CreateUserRequest, userContext: UserContext): Promise<CreateUserResponse> {
140
-
141
- // Extract data from the request (already validated)
142
- const { name, email } = req;
143
-
144
- // Your business logic here
145
- ...
146
-
147
- // Return the response
148
- return {
149
- id: newUserId,
150
- name: name,
151
- email: email
152
- };
153
- }
154
-
155
- public parseRequest(req: Request): CreateUserRequest {
156
- // Validate and parse the incoming Express request
157
- if (!req.body.name) throw new ValidationError(400, "Name is required");
158
- if (!req.body.email) throw new ValidationError(400, "Email is required");
159
-
160
- return {
161
- name: req.body.name,
162
- email: req.body.email
163
- };
164
- }
165
- }
166
-
167
- interface CreateUserRequest extends TotoRequest {
168
- name: string;
169
- email: string;
170
- }
171
-
172
- interface CreateUserResponse {
173
- id: string;
174
- name: string;
175
- email: string;
176
- }
177
- ```
178
-
179
- #### Register Your Delegate
180
- You can now register your delegate with its endpoint (path, route) in the `TotoMicroserviceConfiguration` object that we saw earlier.
181
-
182
- ```typescript
183
- const config: TotoMicroserviceConfiguration = {
184
- serviceName: "toto-ms-ex1",
185
- basePath: '/ex1',
186
- environment: ...,
187
- ...
188
- apiConfiguration: {
189
- apiEndpoints: [
190
- { method: 'POST', path: '/users', delegate: CreateUserDelegate }
191
- ]
192
- },
193
- };
194
- ```
195
-
196
- #### Exposing the OpenAPI Spec through Swagger UI
197
- **If you have** an OpenAPI Spec defined for your microservice, you can expose it through Swagger UI. <br>
198
- To do that, you need to add the following to your `TotoMicroserviceConfiguration`, in the `apiConfiguration` section:
199
-
200
- ```typescript
201
- apiConfiguration: {
202
- ...
203
- openAPISpecification: { localSpecsFilePath: './openapi.yaml' }
204
- },
205
- ```
206
-
207
- After this the API Documentation will be available at: `http(s)://<your-microservice-host>:<port>/<base-path>/apidocs` <br>
208
-
209
- **IMPORTANT** - API Documentation is ALSO available as a JSON endpoint at: `http(s)://<your-microservice-host>:<port>/<base-path>/jsondocs` <br>
210
-
211
- ---
212
-
213
- ### 3.3. Use a Message Bus
214
-
215
- The Message Bus enables event-driven communication between microservices.<br>
216
- It supports both PUSH (webhook-based from cloud Pub/Sub) and PULL (polling) delivery models, depending on your cloud provider and configuration.
217
-
218
- #### 3.3.1. React to Messages
219
-
220
- Message handlers are the primary way to react to events.
221
-
222
- ##### Create a Message Handler
223
-
224
- Create a handler by **extending** `TotoMessageHandler` and implementing the required methods:
225
-
226
- ```typescript
227
- import { TotoMessageHandler, TotoMessage, ProcessingResponse } from 'totoms';
228
-
229
- class TopicRefreshedEventHandler extends TotoMessageHandler {
230
-
231
- getHandledMessageType(): string {
232
- // Return the message type this handler processes
233
- return "topicRefreshed";
234
- }
235
-
236
- async processMessage(message: TotoMessage): Promise<ProcessingResponse> {
237
- // Access message metadata
238
- const correlationId = message.correlationId;
239
- const messageId = message.id;
240
-
241
- // Extract event data
242
- const topicName = message.payload.name;
243
- const blogUrl = message.payload.blogURL;
244
- const user = message.payload.user;
245
-
246
- // Your handler has access to context
247
- this.logger.compute(correlationId, `Processing topic refresh for: ${topicName}`);
248
-
249
- // Perform your business logic
250
- await this.refreshTopic(topicName, blogUrl, user);
251
-
252
- // Return success or failure
253
- return { success: true };
254
- }
255
-
256
- private async refreshTopic(name: string, url: string, user: string) {
257
- // Implementation here
258
- }
259
- }
260
- ```
261
-
262
- ##### Register a Message Handler
263
-
264
- Register your message handlers with the message bus configuration.
265
-
266
- **IMPORTANT NOTE:** <br>
267
- * When using PubSub infrastructure, you need to register topics. <br>
268
- Topics are registered by giving them:
269
- * A `logical name` which is the name that will be used in the application to reference the topic.
270
- * A topic identifier (e.g., ARN on AWS or fully-qualified Topic Name on GCP)
271
-
272
- ```typescript
273
- import { TotoMessageBus, MessageHandlerRegistrationOptions } from 'totoms';
274
-
275
- const messageBus = new TotoMessageBus(config, environment);
276
-
277
- // Register topics
278
- messageBus.registerTopic({
279
- logicalName: "topic-events",
280
- topicName: process.env.TOPIC_EVENTS_TOPIC_NAME! // From environment or secrets
281
- });
282
-
283
- // Register message handlers
284
- const handlerOptions: MessageHandlerRegistrationOptions = {
285
- topic: { logicalName: "topic-events" }
286
- };
287
-
288
- messageBus.registerMessageHandler(
289
- new TopicRefreshedEventHandler(),
290
- handlerOptions
291
- );
292
- ```
293
-
294
- When the microservice starts, it automatically subscribes to the configured topics and routes incoming messages to the appropriate handlers based on their message type.
295
-
296
- #### 3.3.2. Publish Messages
297
-
298
- You can always publish messages to topics.
299
-
300
- **NOTE:**
301
- * In the Message Destination, the topic is the **logical name of the topic** (see above).
302
-
303
- ```typescript
304
- import { TotoMessage, MessageDestination } from 'totoms';
305
-
306
- async function publishTopicUpdate(messageBus: any, topicId: string, topicName: string) {
307
- // Create the message
308
- const message = new TotoMessage({
309
- type: "topicUpdated",
310
- correlationId: "correlation-id-123",
311
- id: topicId,
312
- payload: {
313
- name: topicName,
314
- timestamp: new Date().toISOString()
315
- }
316
- });
317
-
318
- const destination: MessageDestination = {
319
- topicName: "topic-events"
320
- };
321
-
322
- await messageBus.publishMessage(destination, message);
323
- }
324
- ```
325
-
326
- ##### Getting Access to the Message Bus
327
-
328
- There are different ways to get access to the Message Bus instance:
329
-
330
- * Through the `TotoMicroservice` singleton: <br>
331
- `TotoMicroservice.getInstance().messageBus`
332
-
333
- * Through an existing instance of `TotoMicroservice`
334
-
335
- * In a `TotoMessageHandler` you will have `messageBus` as an instance variable: <br>
336
- `this.messageBus`
337
-
338
- * In a `TotoDelegate`, you can access it through the config or by maintaining a reference in your application
339
-
340
- ---
341
-
342
- ### 3.4. Expose MCP Tools
343
-
344
- The SDK now supports exposing delegates as **Model Context Protocol (MCP) Tools**. This allows your microservice to be consumed by AI agents and other MCP-compatible clients.
345
-
346
- #### Creating an MCP-Enabled Delegate
347
-
348
- To expose a delegate as an MCP tool, extend `TotoMCPDelegate` instead of `TotoDelegate` and implement the `getToolDefinition()` method:
349
-
350
- ```typescript
351
- import { TotoMCPDelegate, UserContext, TotoRequest } from 'totoms';
352
- import { TotoMCPToolDefinition } from 'totoms';
353
- import { Request } from 'express';
354
- import z from 'zod';
355
-
356
- export class GetTopics extends TotoMCPDelegate<GetTopicsRequest, GetTopicsResponse> {
357
-
358
- public getToolDefinition(): TotoMCPToolDefinition {
359
- return {
360
- name: "getTopics",
361
- title: "Get user's topics in Tome",
362
- description: "Retrieves all topics associated with the authenticated user.",
363
- inputSchema: z.object({}) // Define the input schema using Zod
364
- }
365
- }
366
-
367
- async do(req: GetTopicsRequest, userContext: UserContext): Promise<GetTopicsResponse> {
368
- const user = userContext.email;
369
-
370
- // Your business logic here
371
- const topics = await this.fetchTopicsForUser(user);
372
-
373
- return { topics };
374
- }
375
-
376
- public parseRequest(req: Request): GetTopicsRequest {
377
- // Parse Express request (for REST API usage)
378
- return {};
379
- }
380
-
381
- private async fetchTopicsForUser(user: string) {
382
- // Implementation
383
- ...
384
- }
385
- }
386
-
387
- interface GetTopicsRequest extends TotoRequest {}
388
-
389
- interface GetTopicsResponse {
390
- topics: any[];
391
- }
392
- ```
393
-
394
- #### Registering MCP Tools
395
-
396
- Register your MCP-enabled delegates in the `mcpConfiguration` section of your microservice configuration:
397
-
398
- ```typescript
399
- import { TotoMicroservice, TotoMicroserviceConfiguration } from 'totoms';
400
- import { GetTopics } from './dlg/GetTopics';
401
- import { GetTopic } from './dlg/GetTopic';
402
-
403
- const config: TotoMicroserviceConfiguration = {
404
- serviceName: "tome-ms-topics",
405
- basePath: '/tometopics',
406
- environment: ...,
407
- apiConfiguration: {
408
- apiEndpoints: [
409
- { method: 'GET', path: '/topics', delegate: GetTopics }
410
- ]
411
- },
412
- mcpConfiguration: {
413
- enableMCP: true,
414
- serverConfiguration: {
415
- name: "Tome Topics MCP Server",
416
- tools: [
417
- GetTopics,
418
- GetTopic
419
- ]
420
- }
421
- }
422
- };
423
- ```
424
-
425
- #### Key Points
426
-
427
- * **Dual Purpose**: Delegates extending `TotoMCPDelegate` can serve both as REST API endpoints AND as MCP tools
428
- * **Tool Definition**: The `getToolDefinition()` method defines how the tool appears to MCP clients
429
- * **Input Schema**: Use Zod schemas to define and validate tool inputs
430
- * **Custom Processing**: Override `processToolRequest()` if you need custom logic for MCP tool invocations that differs from REST API handling
431
-
432
- ---
433
-
434
- ### 3.5. Load Secrets
435
-
436
- The SDK handles secret loading from your cloud provider automatically. Access secrets through the configuration or use the `SecretsManager` directly:
437
-
438
- ```typescript
439
- import { SecretsManager } from 'totoms';
440
-
441
- const secrets = new SecretsManager({ hyperscaler: "aws" });
442
-
443
- // Load a secret by name
444
- const apiKey = await secrets.getSecret("api-key");
445
- const databaseUrl = await secrets.getSecret("database-url");
446
- ```
447
-
448
- Secrets are typically stored as environment variable names or secret manager references, depending on your deployment environment.
449
-
450
- ---
451
-
452
- ### 3.6. Custom Configurations
453
-
454
- You can define your own custom configurations by extending the `TotoControllerConfig` base class.
455
-
456
- An example:
457
-
458
- ```typescript
459
- import { TotoControllerConfig } from 'totoms';
460
-
461
- export class MyServiceConfig extends TotoControllerConfig {
462
-
463
- apiKey: string | undefined;
464
-
465
- async load(): Promise<void> {
466
- // Load secrets using the secrets manager
467
- this.apiKey = await this.secretsManager.getSecret("my-api-key");
468
- }
469
-
470
- getMongoSecretNames() {
471
- // Return null if your service doesn't use MongoDB
472
- return null;
473
- }
474
- }
475
- ```
476
-
477
- What you can do with a Custom Configuration:
478
-
479
- 1. **Load Secrets** <br>
480
- You can do that by overriding the `load()` async method and using `this.secretsManager.getSecret("your-secret-name")` to load secrets.
481
-
482
- 2. **Configure MongoDB** <br>
483
- Override `getMongoSecretNames()`, `getDBName()`, and `getCollections()` to configure MongoDB integration.
484
-
485
- 3. **Custom Authentication** <br>
486
- Override `getCustomAuthVerifier()` to provide custom authentication logic.
487
-
488
- ## Core Components
489
-
490
- ### TotoAPIController
491
- The main controller for building REST APIs with Express. Provides:
492
- - Automatic route registration
493
- - Built-in validation
494
- - CORS support
495
- - Health check endpoints
496
- - File upload support
497
- - API documentation generation
498
-
499
- ### TotoMicroservice
500
- High-level wrapper that initializes the entire microservice stack including API controller, message bus, and environment configuration.
501
-
502
- ### TotoMessageBus
503
- Unified interface for pub/sub messaging across cloud platforms:
504
- - **AWS**: SNS/SQS
505
- - **GCP**: Cloud Pub/Sub
506
- - **Azure**: Service Bus (in development)
507
-
508
- ### TotoControllerConfig
509
- Base configuration class for microservices with support for:
510
- - MongoDB connection management
511
- - Authentication settings
512
- - Secrets management
513
- - Custom validators
514
-
515
- ### Logger
516
- Structured logging with correlation ID support for request tracing.
517
-
518
- ### Validator
519
- Request validation framework with support for:
520
- - JWT token validation
521
- - Google OAuth
522
- - Custom validation logic
523
-
524
- ## Cloud Platform Support
525
-
526
- ### AWS
527
- - **Messaging**: SNS (topics), SQS (queues)
528
- - **Secrets**: AWS Secrets Manager
529
- - **Region Configuration**: Configurable per service
530
-
531
- ### GCP
532
- - **Messaging**: Cloud Pub/Sub
533
- - **Secrets**: Secret Manager
534
- - **Project Configuration**: Uses default project credentials
535
-
536
- ### Azure
537
- - **Messaging**: Service Bus (in development)
538
- - **Secrets**: Key Vault (in development)
539
-
540
- ## License
541
-
542
- MIT
543
-
544
- ## Author
545
-
546
- nicolasances
547
-
548
- ## Contributing
549
-
550
- Contributions are welcome! Please feel free to submit a Pull Request to the [toto-microservice-sdk repository](https://github.com/nicolasances/toto-microservice-sdk).
551
-
552
- ## Related Projects
553
-
554
- - [Toto Ecosystem](https://github.com/nicolasances/toto)
555
- - [Python Toto Microservice SDK](../python)
1
+ # Toto Microservice SDK - NodeJS
2
+
3
+ The Toto Microservice SDK is a framework for building cloud-agnostic microservices. <br>
4
+ This is the NodeJS SDK documentation.
5
+
6
+ ## Table of Contents
7
+
8
+ 1. [Installation](#1-installation)
9
+ 2. [Overview](#2-overview)
10
+ 3. [Usage](#3-usage)
11
+ - [3.1. The Toto Microservice Configuration](#31-the-toto-microservice-configuration)
12
+ - [3.2. Create and Register APIs](#32-create-and-register-apis)
13
+ * [Exposing the OpenAPI Spec through Swagger UI](#exposing-the-openapi-spec-through-swagger-ui)
14
+ - [3.3. Use a Message Bus](#33-use-a-message-bus)
15
+ - [3.4. Expose MCP Tools](#34-expose-mcp-tools)
16
+ - [3.5. Load Secrets](#35-load-secrets)
17
+ - [3.6. Custom Configurations](#36-custom-configurations)
18
+ - [3.7. Publish an Agent](#37-publish-an-agent)
19
+
20
+ Other:
21
+ * [Build and Deploy on NPM](./docs/buildpublish.md)
22
+
23
+ ## 1. Installation
24
+
25
+ ```bash
26
+ npm install totoms
27
+ ```
28
+
29
+ ### Cloud-Specific Dependencies
30
+
31
+ Install the peer dependencies for your target cloud platform:
32
+
33
+ **AWS:**
34
+ ```bash
35
+ npm install @aws-sdk/client-secrets-manager @aws-sdk/client-sns @aws-sdk/client-sqs
36
+ ```
37
+
38
+ **GCP:**
39
+ ```bash
40
+ npm install @google-cloud/pubsub @google-cloud/secret-manager
41
+ ```
42
+
43
+ ## 2. Overview
44
+
45
+ Everything starts with `TotoMicroservice` and the `TotoMicroserviceConfiguration`.<br>
46
+ `TotoMicroservice` is the main orchestrator that coordinates your entire microservice. It initializes and manages:
47
+
48
+ - **API Controller & API Endpoints**: Express-based REST API setup with automatic endpoint registration
49
+ - **Message Bus & Message Handlers**: Event-driven communication via Pub/Sub and Queues. Registration and routing of event handlers to appropriate topics.
50
+ - **Secrets Management**: Automatic loading of secrets from your cloud provider
51
+ - **Service Lifecycle**: Initialization, startup, and shutdown management
52
+
53
+ The configuration is **declarative**. The goal is to make it very simple to configure a full microservice, with a syntax that will look like this:
54
+
55
+ ```typescript
56
+ import { getHyperscalerConfiguration, SupportedHyperscalers, TotoMicroservice, TotoMicroserviceConfiguration } from 'totoms';
57
+ import { ControllerConfig } from "./Config";
58
+ import { SayHello } from './dlg/ExampleDelegate';
59
+
60
+
61
+ const config: TotoMicroserviceConfiguration = {
62
+ serviceName: "toto-ms-ex1",
63
+ basePath: '/ex1',
64
+ environment: {
65
+ hyperscaler: process.env.HYPERSCALER as SupportedHyperscalers || "aws",
66
+ hyperscalerConfiguration: getHyperscalerConfiguration()
67
+ },
68
+ customConfiguration: ControllerConfig,
69
+ apiConfiguration: {
70
+ apiEndpoints: [
71
+ { method: 'GET', path: '/hello', delegate: SayHello }
72
+ ],
73
+ apiOptions: { noCorrelationId: true }
74
+ },
75
+ };
76
+
77
+ TotoMicroservice.init(config).then(microservice => {
78
+ microservice.start();
79
+ });
80
+ ```
81
+
82
+ A **few things you should pay attention to**:
83
+ * `ControllerConfig` - that's your custom configuration class, that you can use to do any type of custom initialization and work (e.g. loading secrets). <br>
84
+ You can find [more details in this section](#31-the-toto-microservice-configuration)
85
+
86
+ The `TotoMicroserviceConfiguration` object specifies:
87
+
88
+ - **Service Metadata**: Service name and base path for API endpoints
89
+ - **Environment**: Cloud provider (AWS, GCP, Azure) information
90
+ - **API Configuration**: REST endpoints with their handlers
91
+ - **Message Bus Configuration**: Topics to subscribe to and message handlers
92
+ - **Custom Configuration**: Your application-specific settings
93
+
94
+ ## 3. Usage
95
+
96
+ ### 3.1. The Toto Microservice Configuration
97
+
98
+ The microservice is configured through the `TotoMicroserviceConfiguration` object and the `TotoControllerConfig` base class. <br>
99
+ As seen above, you need to define a **Custom Configuration Class** that extends the `TotoControllerConfig` base class as shown below here.
100
+
101
+ ```typescript
102
+ import { TotoControllerConfig } from 'totoms';
103
+
104
+ export class ControllerConfig extends TotoControllerConfig {
105
+
106
+ getMongoSecretNames(): { userSecretName: string; pwdSecretName: string; } | null {
107
+ return null;
108
+ }
109
+
110
+ getProps(): APIOptions {
111
+ return {}
112
+ }
113
+
114
+ }
115
+ ```
116
+
117
+ Some things to **note**:
118
+ * The `getMongoSecretNames()` method allows you to define the name of the Secrets containing user and pswd of your Mongo DB, if you choose to use it (stored in the Cloud Secrets Manager, depending on the cloud you're deploying to).
119
+ * The `getProps()` method allows you to do some overrides (e.g. no authentication for this service). You can explore the properties, they're well documented in the SDK.
120
+
121
+ ### 3.2. Create and Register APIs
122
+
123
+ Your microservice exposes REST API endpoints using Express. <br>
124
+ Endpoints are defined when creating the API controller and are automatically set up.
125
+
126
+ #### Create a Toto Delegate
127
+
128
+ Every endpoint needs to be managed by a **Toto Delegate**. <br>
129
+ Toto Delegates extend the `TotoDelegate` abstract class.
130
+
131
+ This is how you define a Toto Delegate. <br>
132
+ *The following example shows a delegate that processes user creation*.
133
+
134
+ ```typescript
135
+ import { TotoDelegate, UserContext, ValidationError, TotoRequest } from 'totoms';
136
+ import { Request } from 'express';
137
+
138
+ class CreateUserDelegate extends TotoDelegate<CreateUserRequest, CreateUserResponse> {
139
+
140
+ async do(req: CreateUserRequest, userContext: UserContext): Promise<CreateUserResponse> {
141
+
142
+ // Extract data from the request (already validated)
143
+ const { name, email } = req;
144
+
145
+ // Your business logic here
146
+ ...
147
+
148
+ // Return the response
149
+ return {
150
+ id: newUserId,
151
+ name: name,
152
+ email: email
153
+ };
154
+ }
155
+
156
+ public parseRequest(req: Request): CreateUserRequest {
157
+ // Validate and parse the incoming Express request
158
+ if (!req.body.name) throw new ValidationError(400, "Name is required");
159
+ if (!req.body.email) throw new ValidationError(400, "Email is required");
160
+
161
+ return {
162
+ name: req.body.name,
163
+ email: req.body.email
164
+ };
165
+ }
166
+ }
167
+
168
+ interface CreateUserRequest extends TotoRequest {
169
+ name: string;
170
+ email: string;
171
+ }
172
+
173
+ interface CreateUserResponse {
174
+ id: string;
175
+ name: string;
176
+ email: string;
177
+ }
178
+ ```
179
+
180
+ #### Register Your Delegate
181
+ You can now register your delegate with its endpoint (path, route) in the `TotoMicroserviceConfiguration` object that we saw earlier.
182
+
183
+ ```typescript
184
+ const config: TotoMicroserviceConfiguration = {
185
+ serviceName: "toto-ms-ex1",
186
+ basePath: '/ex1',
187
+ environment: ...,
188
+ ...
189
+ apiConfiguration: {
190
+ apiEndpoints: [
191
+ { method: 'POST', path: '/users', delegate: CreateUserDelegate }
192
+ ]
193
+ },
194
+ };
195
+ ```
196
+
197
+ #### Exposing the OpenAPI Spec through Swagger UI
198
+ **If you have** an OpenAPI Spec defined for your microservice, you can expose it through Swagger UI. <br>
199
+ To do that, you need to add the following to your `TotoMicroserviceConfiguration`, in the `apiConfiguration` section:
200
+
201
+ ```typescript
202
+ apiConfiguration: {
203
+ ...
204
+ openAPISpecification: { localSpecsFilePath: './openapi.yaml' }
205
+ },
206
+ ```
207
+
208
+ After this the API Documentation will be available at: `http(s)://<your-microservice-host>:<port>/<base-path>/apidocs` <br>
209
+
210
+ **IMPORTANT** - API Documentation is ALSO available as a JSON endpoint at: `http(s)://<your-microservice-host>:<port>/<base-path>/jsondocs` <br>
211
+
212
+ ---
213
+
214
+ ### 3.3. Use a Message Bus
215
+
216
+ The Message Bus enables event-driven communication between microservices.<br>
217
+ It supports both PUSH (webhook-based from cloud Pub/Sub) and PULL (polling) delivery models, depending on your cloud provider and configuration.
218
+
219
+ #### 3.3.1. React to Messages
220
+
221
+ Message handlers are the primary way to react to events.
222
+
223
+ ##### Create a Message Handler
224
+
225
+ Create a handler by **extending** `TotoMessageHandler` and implementing the required methods:
226
+
227
+ ```typescript
228
+ import { TotoMessageHandler, TotoMessage, ProcessingResponse } from 'totoms';
229
+
230
+ class TopicRefreshedEventHandler extends TotoMessageHandler {
231
+
232
+ getHandledMessageType(): string {
233
+ // Return the message type this handler processes
234
+ return "topicRefreshed";
235
+ }
236
+
237
+ async processMessage(message: TotoMessage): Promise<ProcessingResponse> {
238
+ // Access message metadata
239
+ const correlationId = message.correlationId;
240
+ const messageId = message.id;
241
+
242
+ // Extract event data
243
+ const topicName = message.payload.name;
244
+ const blogUrl = message.payload.blogURL;
245
+ const user = message.payload.user;
246
+
247
+ // Your handler has access to context
248
+ this.logger.compute(correlationId, `Processing topic refresh for: ${topicName}`);
249
+
250
+ // Perform your business logic
251
+ await this.refreshTopic(topicName, blogUrl, user);
252
+
253
+ // Return success or failure
254
+ return { success: true };
255
+ }
256
+
257
+ private async refreshTopic(name: string, url: string, user: string) {
258
+ // Implementation here
259
+ }
260
+ }
261
+ ```
262
+
263
+ ##### Register a Message Handler
264
+
265
+ Register your message handlers with the message bus configuration.
266
+
267
+ **IMPORTANT NOTE:** <br>
268
+ * When using PubSub infrastructure, you need to register topics. <br>
269
+ Topics are registered by giving them:
270
+ * A `logical name` which is the name that will be used in the application to reference the topic.
271
+ * A topic identifier (e.g., ARN on AWS or fully-qualified Topic Name on GCP)
272
+
273
+ ```typescript
274
+ import { TotoMessageBus, MessageHandlerRegistrationOptions } from 'totoms';
275
+
276
+ const messageBus = new TotoMessageBus(config, environment);
277
+
278
+ // Register topics
279
+ messageBus.registerTopic({
280
+ logicalName: "topic-events",
281
+ topicName: process.env.TOPIC_EVENTS_TOPIC_NAME! // From environment or secrets
282
+ });
283
+
284
+ // Register message handlers
285
+ const handlerOptions: MessageHandlerRegistrationOptions = {
286
+ topic: { logicalName: "topic-events" }
287
+ };
288
+
289
+ messageBus.registerMessageHandler(
290
+ new TopicRefreshedEventHandler(),
291
+ handlerOptions
292
+ );
293
+ ```
294
+
295
+ When the microservice starts, it automatically subscribes to the configured topics and routes incoming messages to the appropriate handlers based on their message type.
296
+
297
+ #### 3.3.2. Publish Messages
298
+
299
+ You can always publish messages to topics.
300
+
301
+ **NOTE:**
302
+ * In the Message Destination, the topic is the **logical name of the topic** (see above).
303
+
304
+ ```typescript
305
+ import { TotoMessage, MessageDestination } from 'totoms';
306
+
307
+ async function publishTopicUpdate(messageBus: any, topicId: string, topicName: string) {
308
+ // Create the message
309
+ const message = new TotoMessage({
310
+ type: "topicUpdated",
311
+ correlationId: "correlation-id-123",
312
+ id: topicId,
313
+ payload: {
314
+ name: topicName,
315
+ timestamp: new Date().toISOString()
316
+ }
317
+ });
318
+
319
+ const destination: MessageDestination = {
320
+ topicName: "topic-events"
321
+ };
322
+
323
+ await messageBus.publishMessage(destination, message);
324
+ }
325
+ ```
326
+
327
+ ##### Getting Access to the Message Bus
328
+
329
+ There are different ways to get access to the Message Bus instance:
330
+
331
+ * Through the `TotoMicroservice` singleton: <br>
332
+ `TotoMicroservice.getInstance().messageBus`
333
+
334
+ * Through an existing instance of `TotoMicroservice`
335
+
336
+ * In a `TotoMessageHandler` you will have `messageBus` as an instance variable: <br>
337
+ `this.messageBus`
338
+
339
+ * In a `TotoDelegate`, you can access it through the config or by maintaining a reference in your application
340
+
341
+ ---
342
+
343
+ ### 3.4. Expose MCP Tools
344
+
345
+ The SDK now supports exposing delegates as **Model Context Protocol (MCP) Tools**. This allows your microservice to be consumed by AI agents and other MCP-compatible clients.
346
+
347
+ #### Creating an MCP-Enabled Delegate
348
+
349
+ To expose a delegate as an MCP tool, extend `TotoMCPDelegate` instead of `TotoDelegate` and implement the `getToolDefinition()` method:
350
+
351
+ ```typescript
352
+ import { TotoMCPDelegate, UserContext, TotoRequest } from 'totoms';
353
+ import { TotoMCPToolDefinition } from 'totoms';
354
+ import { Request } from 'express';
355
+ import z from 'zod';
356
+
357
+ export class GetTopics extends TotoMCPDelegate<GetTopicsRequest, GetTopicsResponse> {
358
+
359
+ public getToolDefinition(): TotoMCPToolDefinition {
360
+ return {
361
+ name: "getTopics",
362
+ title: "Get user's topics in Tome",
363
+ description: "Retrieves all topics associated with the authenticated user.",
364
+ inputSchema: z.object({}) // Define the input schema using Zod
365
+ }
366
+ }
367
+
368
+ async do(req: GetTopicsRequest, userContext: UserContext): Promise<GetTopicsResponse> {
369
+ const user = userContext.email;
370
+
371
+ // Your business logic here
372
+ const topics = await this.fetchTopicsForUser(user);
373
+
374
+ return { topics };
375
+ }
376
+
377
+ public parseRequest(req: Request): GetTopicsRequest {
378
+ // Parse Express request (for REST API usage)
379
+ return {};
380
+ }
381
+
382
+ private async fetchTopicsForUser(user: string) {
383
+ // Implementation
384
+ ...
385
+ }
386
+ }
387
+
388
+ interface GetTopicsRequest extends TotoRequest {}
389
+
390
+ interface GetTopicsResponse {
391
+ topics: any[];
392
+ }
393
+ ```
394
+
395
+ #### Registering MCP Tools
396
+
397
+ Register your MCP-enabled delegates in the `mcpConfiguration` section of your microservice configuration:
398
+
399
+ ```typescript
400
+ import { TotoMicroservice, TotoMicroserviceConfiguration } from 'totoms';
401
+ import { GetTopics } from './dlg/GetTopics';
402
+ import { GetTopic } from './dlg/GetTopic';
403
+
404
+ const config: TotoMicroserviceConfiguration = {
405
+ serviceName: "tome-ms-topics",
406
+ basePath: '/tometopics',
407
+ environment: ...,
408
+ apiConfiguration: {
409
+ apiEndpoints: [
410
+ { method: 'GET', path: '/topics', delegate: GetTopics }
411
+ ]
412
+ },
413
+ mcpConfiguration: {
414
+ enableMCP: true,
415
+ serverConfiguration: {
416
+ name: "Tome Topics MCP Server",
417
+ tools: [
418
+ GetTopics,
419
+ GetTopic
420
+ ]
421
+ }
422
+ }
423
+ };
424
+ ```
425
+
426
+ #### MCP Server Endpoint
427
+
428
+ When MCP is enabled, the microservice exposes the MCP server on the following path:
429
+
430
+ ```
431
+ POST {basePath}/mcp
432
+ ```
433
+
434
+ For example, a service with `basePath: '/tometopics'` exposes the MCP server at:
435
+
436
+ ```
437
+ POST /tometopics/mcp
438
+ ```
439
+
440
+ The MCP server uses the **Streamable HTTP transport** in **stateless mode** (no session management). To configure an MCP client (e.g. Claude Desktop, VS Code Copilot, or any MCP-compatible client), point it to this endpoint:
441
+
442
+ ```json
443
+ {
444
+ "mcpServers": {
445
+ "tome-topics": {
446
+ "type": "http",
447
+ "url": "https://<your-host>/tometopics/mcp"
448
+ }
449
+ }
450
+ }
451
+ ```
452
+
453
+ > **Note**: Replace `<your-host>` and `tometopics` with your actual host and `basePath`.
454
+
455
+ #### Key Points
456
+
457
+ * **Dual Purpose**: Delegates extending `TotoMCPDelegate` can serve both as REST API endpoints AND as MCP tools
458
+ * **Tool Definition**: The `getToolDefinition()` method defines how the tool appears to MCP clients
459
+ * **Input Schema**: Use Zod schemas to define and validate tool inputs
460
+ * **Custom Processing**: Override `processToolRequest()` if you need custom logic for MCP tool invocations that differs from REST API handling
461
+
462
+ ---
463
+
464
+ ### 3.5. Load Secrets
465
+
466
+ The SDK handles secret loading from your cloud provider automatically. Access secrets through the configuration or use the `SecretsManager` directly:
467
+
468
+ ```typescript
469
+ import { SecretsManager } from 'totoms';
470
+
471
+ const secrets = new SecretsManager({ hyperscaler: "aws" });
472
+
473
+ // Load a secret by name
474
+ const apiKey = await secrets.getSecret("api-key");
475
+ const databaseUrl = await secrets.getSecret("database-url");
476
+ ```
477
+
478
+ Secrets are typically stored as environment variable names or secret manager references, depending on your deployment environment.
479
+
480
+ ---
481
+
482
+ ### 3.6. Custom Configurations
483
+
484
+ You can define your own custom configurations by extending the `TotoControllerConfig` base class.
485
+
486
+ An example:
487
+
488
+ ```typescript
489
+ import { TotoControllerConfig } from 'totoms';
490
+
491
+ export class MyServiceConfig extends TotoControllerConfig {
492
+
493
+ apiKey: string | undefined;
494
+
495
+ async load(): Promise<void> {
496
+ // Load secrets using the secrets manager
497
+ this.apiKey = await this.secretsManager.getSecret("my-api-key");
498
+ }
499
+
500
+ getMongoSecretNames() {
501
+ // Return null if your service doesn't use MongoDB
502
+ return null;
503
+ }
504
+ }
505
+ ```
506
+
507
+ What you can do with a Custom Configuration:
508
+
509
+ 1. **Load Secrets** <br>
510
+ You can do that by overriding the `load()` async method and using `this.secretsManager.getSecret("your-secret-name")` to load secrets.
511
+
512
+ 2. **Configure MongoDB** <br>
513
+ Override `getMongoSecretNames()`, `getDBName()`, and `getCollections()` to configure MongoDB integration.
514
+
515
+ 3. **Custom Authentication** <br>
516
+ Override `getCustomAuthVerifier()` to provide custom authentication logic.
517
+
518
+ ---
519
+
520
+ ### 3.7. Publish an Agent
521
+
522
+ The SDK supports publishing **Gale Agents** — AI agents that can participate in conversations brokered by [Gale Broker](https://github.com/nicolasances/gale-broker). <br>
523
+ An agent receives a user message, processes it (potentially using an LLM), and returns a response. It can also publish intermediate messages back to the conversation in real time while processing.
524
+
525
+ #### Create a Conversational Agent
526
+
527
+ Extend `GaleConversationalAgent` and implement two required methods:
528
+
529
+ * `getManifest()` returns the agent's identity information
530
+ * `onMessage()` — handles an incoming conversation message and returns the response
531
+
532
+ ```typescript
533
+ import { AgentConversationMessage, GaleConversationalAgent, AgentManifest } from "totoms";
534
+ import { v4 as uuid } from "uuid";
535
+
536
+ export class MyAgent extends GaleConversationalAgent {
537
+
538
+ getManifest(): AgentManifest {
539
+ return {
540
+ agentType: "conversational",
541
+ agentId: "my-agent", // Unique agent identifier
542
+ humanFriendlyName: "My Agent", // Display name shown to users
543
+ }
544
+ }
545
+
546
+ async onMessage(message: AgentConversationMessage): Promise<AgentConversationMessage> {
547
+
548
+ const streamId = uuid();
549
+
550
+ // Optionally publish an intermediate message to the conversation
551
+ // while you are still processing, to give the user early feedback.
552
+ this.publishMessage({
553
+ conversationId: message.conversationId,
554
+ messageId: uuid(),
555
+ agentId: message.agentId,
556
+ message: "Got your message, working on it!",
557
+ actor: "agent",
558
+ stream: { streamId, sequenceNumber: 1, last: false }
559
+ });
560
+
561
+ // ... run your LLM / business logic here ...
562
+ const result = "Here is my answer!";
563
+
564
+ // Return the final response
565
+ return {
566
+ conversationId: message.conversationId,
567
+ messageId: message.messageId,
568
+ agentId: message.agentId,
569
+ message: result,
570
+ actor: "agent",
571
+ stream: { streamId, sequenceNumber: 2, last: true }
572
+ };
573
+ }
574
+ }
575
+ ```
576
+
577
+ Key points:
578
+ * `publishMessage()` lets the agent stream intermediate messages to the client **while still processing**. This is useful to acknowledge receipt or provide progress updates before the final answer is ready.
579
+ * The `stream` field is optional but recommended for conversational agents. Set `last: true` only on the final message.
580
+ * `actor` should always be `"agent"` for messages sent by the agent.
581
+
582
+ #### Register the Agent
583
+
584
+ Add an `agentsConfiguration` block to your `TotoMicroserviceConfiguration`:
585
+
586
+ ```typescript
587
+ import { TotoMicroservice, TotoMicroserviceConfiguration } from "totoms";
588
+ import { MyAgent } from "./agent/MyAgent";
589
+
590
+ const config: TotoMicroserviceConfiguration = {
591
+ serviceName: "my-microservice",
592
+ environment: { ... },
593
+ ...
594
+ agentsConfiguration: {
595
+ agents: [
596
+ MyAgent
597
+ ]
598
+ }
599
+ };
600
+
601
+ TotoMicroservice.init(config).then(microservice => {
602
+ microservice.start();
603
+ });
604
+ ```
605
+
606
+ Multiple agents can be registered in the `agents` array.
607
+
608
+ #### Required Environment Variables
609
+
610
+ To integrate with Gale Broker the following environment variables **must** be set:
611
+
612
+ | Variable | Description |
613
+ |---|---|
614
+ | `GALE_BROKER_URL` | The base URL of the Gale Broker service (e.g. `http://gale-broker:8080/galebroker`). Used to register the agent on startup and to publish messages back to conversations. |
615
+ | `SERVICE_BASE_URL` | The publicly reachable base URL of **this** microservice, including the base path if any (e.g. `https://myservice.example.com/basepath`). Gale Broker uses this to know where to forward incoming conversation messages for the agent. |
616
+
617
+ The SDK will throw an error at startup if either variable is not set when any agent is configured.
618
+
619
+ #### GCP — Required IAM Roles
620
+
621
+ If your agent uses **Vertex AI** (e.g. via `@genkit-ai/google-genai`), the service account running the microservice must have the following IAM role granted in your Terraform configuration:
622
+
623
+ ```hcl
624
+ resource "google_project_iam_member" "my-service_role_aiplatform" {
625
+ project = var.gcp_pid
626
+ role = "roles/aiplatform.user"
627
+ member = format("serviceAccount:%s", google_service_account.my-service-account.email)
628
+ }
629
+ ```
630
+
631
+ Without this role the Vertex AI API calls will fail with a `403 Permission Denied` error.
632
+
633
+ ---
634
+
635
+ ## Core Components
636
+
637
+ ### TotoAPIController
638
+ The main controller for building REST APIs with Express. Provides:
639
+ - Automatic route registration
640
+ - Built-in validation
641
+ - CORS support
642
+ - Health check endpoints
643
+ - File upload support
644
+ - API documentation generation
645
+
646
+ ### TotoMicroservice
647
+ High-level wrapper that initializes the entire microservice stack including API controller, message bus, and environment configuration.
648
+
649
+ ### TotoMessageBus
650
+ Unified interface for pub/sub messaging across cloud platforms:
651
+ - **AWS**: SNS/SQS
652
+ - **GCP**: Cloud Pub/Sub
653
+ - **Azure**: Service Bus (in development)
654
+
655
+ ### TotoControllerConfig
656
+ Base configuration class for microservices with support for:
657
+ - MongoDB connection management
658
+ - Authentication settings
659
+ - Secrets management
660
+ - Custom validators
661
+
662
+ ### Logger
663
+ Structured logging with correlation ID support for request tracing.
664
+
665
+ ### Validator
666
+ Request validation framework with support for:
667
+ - JWT token validation
668
+ - Google OAuth
669
+ - Custom validation logic
670
+
671
+ ## Cloud Platform Support
672
+
673
+ ### AWS
674
+ - **Messaging**: SNS (topics), SQS (queues)
675
+ - **Secrets**: AWS Secrets Manager
676
+ - **Region Configuration**: Configurable per service
677
+
678
+ ### GCP
679
+ - **Messaging**: Cloud Pub/Sub
680
+ - **Secrets**: Secret Manager
681
+ - **Project Configuration**: Uses default project credentials
682
+
683
+ ### Azure
684
+ - **Messaging**: Service Bus (in development)
685
+ - **Secrets**: Key Vault (in development)
686
+
687
+ ## License
688
+
689
+ MIT
690
+
691
+ ## Author
692
+
693
+ nicolasances
694
+
695
+ ## Contributing
696
+
697
+ Contributions are welcome! Please feel free to submit a Pull Request to the [toto-microservice-sdk repository](https://github.com/nicolasances/toto-microservice-sdk).
698
+
699
+ ## Related Projects
700
+
701
+ - [Toto Ecosystem](https://github.com/nicolasances/toto)
702
+ - [Python Toto Microservice SDK](../python)