Agente Strands de TypeScript
Genera un TypeScript Strands Agent para construir agentes de IA con herramientas, y opcionalmente despliégalo en Amazon Bedrock AgentCore Runtime. El generador utiliza tRPC sobre WebSocket para aprovechar el soporte de streaming bidireccional de AgentCore para comunicación en tiempo real y con seguridad de tipos.
¿Qué es Strands?
Sección titulada «¿Qué es Strands?»Strands es un framework ligero para construir agentes de IA. Las características clave incluyen:
- Ligero y personalizable: Bucle de agente simple que no se interpone en tu camino
- Listo para producción: Observabilidad completa, rastreo y opciones de despliegue para escalar
- Agnóstico de modelo y proveedor: Soporta muchos modelos diferentes de varios proveedores
- Herramientas impulsadas por la comunidad: Conjunto poderoso de herramientas contribuidas por la comunidad
- Soporte multi-agente: Técnicas avanzadas como equipos de agentes y agentes autónomos
- Modos de interacción flexibles: Soporte conversacional, streaming y no-streaming
Generar un Strands Agent
Sección titulada «Generar un Strands Agent»Puedes generar un TypeScript Strands Agent de dos maneras:
- Instale el Nx Console VSCode Plugin si aún no lo ha hecho
- Abra la consola Nx en VSCode
- Haga clic en
Generate (UI)en la sección "Common Nx Commands" - Busque
@aws/nx-plugin - ts#strands-agent - Complete los parámetros requeridos
- Haga clic en
Generate
pnpm nx g @aws/nx-plugin:ts#strands-agentyarn nx g @aws/nx-plugin:ts#strands-agentnpx nx g @aws/nx-plugin:ts#strands-agentbunx nx g @aws/nx-plugin:ts#strands-agentTambién puede realizar una ejecución en seco para ver qué archivos se cambiarían
pnpm nx g @aws/nx-plugin:ts#strands-agent --dry-runyarn nx g @aws/nx-plugin:ts#strands-agent --dry-runnpx nx g @aws/nx-plugin:ts#strands-agent --dry-runbunx nx g @aws/nx-plugin:ts#strands-agent --dry-runOpciones
Sección titulada «Opciones»| Parámetro | Tipo | Predeterminado | Descripción |
|---|---|---|---|
| project Requerido | string | - | The project to add the Strands Agent to |
| computeType | string | BedrockAgentCoreRuntime | The type of compute to host your Strands Agent. |
| name | string | - | The name of your Strands Agent (default: agent) |
| iacProvider | string | Inherit | The preferred IaC provider. By default this is inherited from your initial selection. |
Salida del Generador
Sección titulada «Salida del Generador»El generador agregará los siguientes archivos a tu proyecto TypeScript existente:
Directorioyour-project/
Directoriosrc/
Directorioagent/ (o nombre personalizado si se especifica)
- index.ts Punto de entrada para Bedrock AgentCore Runtime
- init.ts Inicialización de tRPC
- router.ts Router de tRPC con procedimientos del agente
- agent.ts Definición principal del agente con herramientas de ejemplo
- client.ts Cliente provisto para invocar tu agente
- agent-core-trpc-client.ts Fábrica de cliente para conectar a agentes en AgentCore Runtime
- agent-core-mcp-client.ts Fábrica de cliente para conectar a servidores MCP en AgentCore Runtime
- Dockerfile Punto de entrada para alojar tu agente (excluido cuando
computeTypeestá establecido enNone)
- package.json Actualizado con dependencias de Strands
- project.json Actualizado con targets de servicio del agente
Infraestructura
Sección titulada «Infraestructura»Dado que este generador proporciona infraestructura como código basada en tu proveedor de iacProvider seleccionado, creará un proyecto en packages/common que incluye los constructos CDK o módulos de Terraform correspondientes.
El proyecto común de infraestructura como código tiene la siguiente estructura:
Directoriopackages/common/constructs
Directoriosrc
Directorioapp/ Constructos para infraestructura específica de un proyecto/generador
- …
Directoriocore/ Constructos genéricos reutilizados por los constructos en
app- …
- index.ts Punto de entrada que exporta los constructos de
app
- project.json Objetivos de compilación y configuración del proyecto
Directoriopackages/common/terraform
Directoriosrc
Directorioapp/ Módulos de Terraform para infraestructura específica de un proyecto/generador
- …
Directoriocore/ Módulos genéricos reutilizados por los módulos en
app- …
- project.json Objetivos de compilación y configuración del proyecto
Para desplegar tu Strands Agent, se generan los siguientes archivos:
Directoriopackages/common/constructs/src
Directorioapp
Directorioagents
Directorio<project-name>
- <project-name>.ts Constructo CDK para desplegar tu agente
- Dockerfile Archivo docker de paso utilizado por el constructo CDK
Directoriopackages/common/terraform/src
Directorioapp
Directorioagents
Directorio<project-name>
- <project-name>.tf Módulo para desplegar tu agente
Directoriocore
Directorioagent-core
- runtime.tf Módulo genérico para desplegar en Bedrock AgentCore Runtime
Trabajar con tu Strands Agent
Sección titulada «Trabajar con tu Strands Agent»tRPC sobre WebSocket
Sección titulada «tRPC sobre WebSocket»El TypeScript Strands Agent utiliza tRPC sobre WebSocket, aprovechando el soporte de streaming bidireccional de AgentCore para habilitar comunicación en tiempo real y con seguridad de tipos entre clientes y tu agente.
Dado que tRPC soporta procedimientos Query, Mutation y Subscription sobre WebSocket, puedes definir cualquier número de procedimientos. Por defecto, se define un único procedimiento de suscripción llamado invoke en router.ts.
Agregar Herramientas
Sección titulada «Agregar Herramientas»Las herramientas son funciones que el agente de IA puede llamar para realizar acciones. Puedes agregar nuevas herramientas en el archivo agent.ts:
import { Agent, tool } from '@strands-agents/sdk';import z from 'zod';
const letterCounter = tool({ name: 'letter_counter', description: 'Count occurrences of a specific letter in a word', inputSchema: z.object({ word: z.string().describe('The input word to search in'), letter: z.string().length(1).describe('The specific letter to count'), }), callback: (input) => { const { word, letter } = input; const count = word.toLowerCase().split(letter.toLowerCase()).length - 1; return `The letter '${letter}' appears ${count} time(s) in '${word}'`; },});
// Add tools to your agentexport const agent = new Agent({ systemPrompt: 'You are a helpful assistant with access to various tools.', tools: [letterCounter],});El framework Strands maneja automáticamente:
- Validación de entrada usando esquemas Zod
- Generación de esquema JSON para llamadas a herramientas
- Manejo de errores y formateo de respuestas
Configuración de Modelo
Sección titulada «Configuración de Modelo»Por defecto, los agentes Strands usan Claude 4 Sonnet, pero puedes cambiar fácilmente entre proveedores de modelos:
import { Agent } from '@strands-agents/sdk';import { BedrockModel } from '@strands-agents/sdk/models/bedrock';import { OpenAIModel } from '@strands-agents/sdk/models/openai';
// Use Bedrockconst bedrockModel = new BedrockModel({ modelId: 'anthropic.claude-sonnet-4-20250514-v1:0',});let agent = new Agent({ model: bedrockModel });let response = await agent.invoke('What can you help me with?');
// Alternatively, use OpenAI by just switching model providerconst openaiModel = new OpenAIModel({ apiKey: process.env.OPENAI_API_KEY, modelId: 'gpt-4o',});agent = new Agent({ model: openaiModel });response = await agent.invoke('What can you help me with?');Consulta la documentación de Strands sobre proveedores de modelos para más opciones de configuración.
Consumir Servidores MCP
Sección titulada «Consumir Servidores MCP»Puedes agregar herramientas desde servidores MCP a tu agente Strands.
Para consumir Servidores MCP que has creado usando los generadores py#mcp-server o ts#mcp-server (u otros alojados en Bedrock AgentCore Runtime), se genera una fábrica de cliente para ti en agent-core-mcp-client.ts.
Puedes actualizar la inicialización de tu agente en agent.ts para crear clientes MCP y agregar herramientas. El siguiente ejemplo muestra cómo realizar esto con autenticación IAM (SigV4):
import { Agent } from '@strands-agents/sdk';import { AgentCoreMcpClient } from './agent-core-mcp-client.js';
const mcpClient = AgentCoreMcpClient.withIamAuth({ agentRuntimeArn: process.env.MCP_AGENTCORE_RUNTIME_ARN!, region: process.env.AWS_REGION || 'us-west-2', sessionId: 'my-session-id',});
export const agent = new Agent({ systemPrompt: '...', tools: [mcpClient],});Con el ejemplo de autenticación IAM anterior, necesitamos configurar dos cosas en nuestra infraestructura. Primero, necesitamos agregar la variable de entorno que nuestro agente está consumiendo para el ARN de AgentCore Runtime de nuestro servidor MCP, y segundo necesitamos otorgar permisos a nuestro agente para invocar el servidor MCP. Esto se puede lograr de la siguiente manera:
import { MyProjectAgent, MyProjectMcpServer } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { const mcpServer = new MyProjectMcpServer(this, 'MyProjectMcpServer');
const agent = new MyProjectAgent(this, 'MyProjectAgent', { environmentVariables: { MCP_AGENTCORE_RUNTIME_ARN: mcpServer.agentCoreRuntime.agentRuntimeArn, }, });
mcpServer.agentCoreRuntime.grantInvoke(agent.agentCoreRuntime); }}# MCP Servermodule "my_project_mcp_server" { source = "../../common/terraform/src/app/mcp-servers/my-project-mcp-server"}
# Agentmodule "my_project_agent" { source = "../../common/terraform/src/app/agents/my-project-agent"
env = { MCP_AGENTCORE_RUNTIME_ARN = module.my_project_mcp_server.agent_core_runtime_arn }
additional_iam_policy_statements = [ { Effect = "Allow" Action = [ "bedrock-agentcore:InvokeAgentRuntime" ] Resource = [ module.my_project_mcp_server.agent_core_runtime_arn, "${module.my_project_mcp_server.agent_core_runtime_arn}/*" ] } ]}Para una guía más profunda sobre cómo escribir agentes Strands, consulta la documentación de Strands.
Ejecutar tu Strands Agent
Sección titulada «Ejecutar tu Strands Agent»Desarrollo Local
Sección titulada «Desarrollo Local»El generador configura un target llamado <your-agent-name>-serve, que inicia tu Strands Agent localmente para desarrollo y pruebas.
pnpm nx run your-project:agent-serveyarn nx run your-project:agent-servenpx nx run your-project:agent-servebunx nx run your-project:agent-serveEste comando usa tsx --watch para reiniciar automáticamente el servidor cuando cambian los archivos. El agente estará disponible en http://localhost:8081 (o el puerto asignado si tienes múltiples agentes).
Desplegar tu Strands Agent en Bedrock AgentCore Runtime
Sección titulada «Desplegar tu Strands Agent en Bedrock AgentCore Runtime»Infraestructura como Código
Sección titulada «Infraestructura como Código»Si seleccionaste BedrockAgentCoreRuntime para computeType, se genera la infraestructura CDK o Terraform relevante que puedes usar para desplegar tu Strands Agent en Amazon Bedrock AgentCore Runtime.
Se genera una construcción CDK para tu agente, nombrada según el name que elegiste al ejecutar el generador, o <ProjectName>Agent por defecto.
Puedes usar esta construcción CDK en una aplicación CDK:
import { MyProjectAgent } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { // Add the agent to your stack const agent = new MyProjectAgent(this, 'MyProjectAgent');
// Grant permissions to invoke the relevant models in bedrock agent.agentCoreRuntime.addToRolePolicy( new PolicyStatement({ actions: [ 'bedrock:InvokeModel', 'bedrock:InvokeModelWithResponseStream', ], // You can scope the below down to the specific models you use resources: [ 'arn:aws:bedrock:*:*:foundation-model/*', 'arn:aws:bedrock:*:*:inference-profile/*', ], }), ); }}Se genera un módulo Terraform para ti, nombrado según el name que elegiste al ejecutar el generador, o <ProjectName>-agent por defecto.
Puedes usar este módulo terraform en un proyecto Terraform:
# Agentmodule "my_project_agent" { # Relative path to the generated module in the common/terraform project source = "../../common/terraform/src/app/agents/my-project-agent"
# Grant permissions to invoke the relevant models in bedrock additional_iam_policy_statements = [ { Effect = "Allow" Action = [ "bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream" ] # You can scope the below down to the specific models you use Resource = [ "arn:aws:bedrock:*:*:foundation-model/*", "arn:aws:bedrock:*:*:inference-profile/*" ] } ]}Autenticación
Sección titulada «Autenticación»Por defecto, tu Strands Agent estará protegido usando autenticación IAM, simplemente despliégalo sin ningún argumento:
import { MyProjectAgent } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { new MyProjectAgent(this, 'MyProjectAgent'); }}Puedes otorgar acceso para invocar tu agente en Bedrock AgentCore Runtime usando el método grantInvoke, por ejemplo:
import { MyProjectAgent } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { const agent = new MyProjectAgent(this, 'MyProjectAgent'); const lambdaFunction = new Function(this, ...);
agent.agentCoreRuntime.grantInvoke(lambdaFunction); }}# Agentmodule "my_project_agent" { # Relative path to the generated module in the common/terraform project source = "../../common/terraform/src/app/agents/my-project-agent"}Para otorgar acceso para invocar tu agente, necesitarás agregar una política como la siguiente, haciendo referencia a la salida module.my_project_agent.agent_core_runtime_arn:
{ Effect = "Allow" Action = [ "bedrock-agentcore:InvokeAgentRuntime" ] Resource = [ module.my_project_agent.agent_core_runtime_arn, "${module.my_project_agent.agent_core_runtime_arn}/*" ]}Autenticación Cognito JWT
Sección titulada «Autenticación Cognito JWT»Lo siguiente demuestra cómo configurar la autenticación Cognito para tu agente.
Para configurar la autenticación JWT usando Cognito, usa el método de fábrica RuntimeAuthorizerConfiguration.usingCognito():
import { MyProjectAgent } from ':my-scope/common-constructs';import { RuntimeAuthorizerConfiguration } from '@aws-cdk/aws-bedrock-agentcore-alpha';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { const userPool = new UserPool(this, 'UserPool'); const client = userPool.addClient('Client', { authFlows: { userPassword: true, }, });
new MyProjectAgent(this, 'MyProjectAgent', { authorizerConfiguration: RuntimeAuthorizerConfiguration.usingCognito( userPool, [client], ), }); }}Alternativamente, para autenticación JWT personalizada con tu propio proveedor OIDC, usa RuntimeAuthorizerConfiguration.usingJWT():
import { MyProjectAgent } from ':my-scope/common-constructs';import { RuntimeAuthorizerConfiguration } from '@aws-cdk/aws-bedrock-agentcore-alpha';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { new MyProjectAgent(this, 'MyProjectAgent', { authorizerConfiguration: RuntimeAuthorizerConfiguration.usingJWT( 'https://example.com/.well-known/openid-configuration', ['client1', 'client2'], // Allowed Client IDs (optional) ['audience1'], // Allowed Audiences (optional) ), }); }}Para configurar la autenticación JWT, puedes editar tu módulo de agente para configurar la variable authorizer_configuration de la siguiente manera:
data "aws_region" "current" {}
locals { aws_region = data.aws_region.current.id
# Replace with your user pool and client ids or expose as variables user_pool_id = "xxx" user_pool_client_ids = ["yyy"]}
module "agent_core_runtime" { source = "../../../core/agent-core" agent_runtime_name = "MyProjectAgent" docker_image_tag = "my-scope-my-project-agent:latest" server_protocol = "HTTP" authorizer_configuration = { custom_jwt_authorizer = { discovery_url = "https://cognito-idp.${local.aws_region}.amazonaws.com/${local.user_pool_id}/.well-known/openid-configuration" allowed_clients = local.user_pool_client_ids } } env = var.env additional_iam_policy_statements = var.additional_iam_policy_statements tags = var.tags}Target Bundle
Sección titulada «Target Bundle»El generador configura automáticamente un objetivo bundle que utiliza Rolldown para crear un paquete de despliegue:
pnpm nx run <project-name>:bundleyarn nx run <project-name>:bundlenpx nx run <project-name>:bundlebunx nx run <project-name>:bundleLa configuración de Rolldown se encuentra en rolldown.config.ts, con una entrada por cada bundle a generar. Rolldown gestiona la creación de múltiples bundles en paralelo si están definidos.
El target bundle usa index.ts como punto de entrada para el servidor WebSocket para alojar en Bedrock AgentCore Runtime.
Target Docker
Sección titulada «Target Docker»El generador configura un target <your-agent-name>-docker que ejecuta el servidor WebSocket empaquetado en el puerto 8080 según el contrato de runtime de AgentCore.
También se genera un target docker que ejecuta la construcción docker para todos los agentes si tienes múltiples definidos.
Observabilidad
Sección titulada «Observabilidad»Tu agente está configurado automáticamente con observabilidad usando el AWS Distro for Open Telemetry (ADOT), configurando auto-instrumentación en tu Dockerfile.
Puedes encontrar trazas en la Consola AWS CloudWatch, seleccionando “GenAI Observability” en el menú. Ten en cuenta que para que las trazas se completen necesitarás habilitar Transaction Search.
Para más detalles, consulta la documentación de AgentCore sobre observabilidad.
Invocar tu Strands Agent
Sección titulada «Invocar tu Strands Agent»La comunicación del agente se transmite vía tRPC sobre WebSocket. Como tal, se recomienda usar la fábrica de cliente con seguridad de tipos generada en client.ts.
Invocar el Servidor Local
Sección titulada «Invocar el Servidor Local»Puedes invocar un agente ejecutándose localmente usando el método de fábrica .local de la fábrica de cliente.
Puedes, por ejemplo, crear un archivo llamado scripts/test.ts en tu workspace que importe el cliente:
import { AgentClient } from '../packages/<project>/src/agent/client.js';
const client = AgentClient.local({ url: 'http://localhost:8081/ws' });
client.invoke.subscribe({ message: 'what is 1 plus 1?' }, { onData: console.log });Invocar el Agente Desplegado
Sección titulada «Invocar el Agente Desplegado»Para invocar tu Agente desplegado en Bedrock AgentCore Runtime, puedes enviar una solicitud POST al endpoint del plano de datos de Bedrock AgentCore Runtime con tu ARN de runtime codificado en URL.
Puedes obtener el ARN de runtime desde tu infraestructura de la siguiente manera:
import { CfnOutput } from 'aws-cdk-lib';import { MyProjectAgent } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { const agent = new MyProjectAgent(this, 'MyProjectAgent');
new CfnOutput(this, 'AgentArn', { value: agent.agentCoreRuntime.agentRuntimeArn, }); }}# Agentmodule "my_project_agent" { # Relative path to the generated module in the common/terraform project source = "../../common/terraform/src/app/agents/my-project-agent"}
output "agent_arn" { value = module.my_project_agent.agent_core_runtime_arn}El ARN tendrá el siguiente formato: arn:aws:bedrock-agentcore:<region>:<account>:runtime/<agent-runtime-id>.
Luego puedes codificar el ARN en URL reemplazando : con %3A y / con %2F.
La URL del plano de datos de Bedrock AgentCore Runtime para invocar el agente es la siguiente:
https://bedrock-agentcore.<region>.amazonaws.com/runtimes/<url-encoded-arn>/invocationsLa forma exacta de invocar esta URL depende del método de autenticación utilizado.
El archivo client.ts generado incluye una fábrica de cliente con seguridad de tipos que puede usarse para invocar tu agente desplegado.
Autenticación IAM
Sección titulada «Autenticación IAM»Puedes invocar tu agente desplegado pasando su ARN al método de fábrica withIamAuth:
import { AgentClient } from './agent/client.js';
const client = AgentClient.withIamAuth({ agentRuntimeArn: 'arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent',});
client.invoke.subscribe({ message: 'what is 1 plus 1?' }, { onData: (message) => console.log(message), onError: (error) => console.error(error), onComplete: () => console.log('Done'),});Autenticación JWT / Cognito
Sección titulada «Autenticación JWT / Cognito»Usa el método de fábrica withJwtAuth para autenticar con el token de acceso JWT / Cognito.
const client = AgentClient.withJwtAuth({ agentRuntimeArn: 'arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent', accessTokenProvider: async () => `<access-token>`,});
client.invoke.subscribe({ message: 'what is 1 plus 1?' }, { onData: console.log,});El accessTokenProvider debe devolver el token usado para autenticar la solicitud. Puedes, por ejemplo, obtener un token dentro de este método para asegurar que las credenciales frescas se reutilicen cuando tRPC reinicie una conexión WebSocket. Lo siguiente demuestra usar el AWS SDK para obtener el token de Cognito:
import { CognitoIdentityProvider } from "@aws-sdk/client-cognito-identity-provider";
const cognito = new CognitoIdentityProvider();
const jwtClient = AgentClient.withJwtAuth({ agentRuntimeArn: 'arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent', accessTokenProvider: async () => { const response = await cognito.adminInitiateAuth({ UserPoolId: '<user-pool-id>', ClientId: '<user-pool-client-id>', AuthFlow: 'ADMIN_NO_SRP_AUTH', AuthParameters: { USERNAME: '<username>', PASSWORD: '<password>', }, }); return response.AuthenticationResult!.AccessToken!; },});Browser
Sección titulada «Browser»Los WebSockets en el navegador no soportan especificar encabezados (aparte de Sec-WebSocket-Protocol), y por lo tanto la fábrica de cliente generada en client.ts no puede usarse en un navegador (esto en realidad resultará en un error de compilación ya que el constructor WebSocket no acepta encabezados como lo hace en NodeJS).
Autenticación JWT / Cognito
Sección titulada «Autenticación JWT / Cognito»Autenticación IAM
Sección titulada «Autenticación IAM»Para invocar tu agente desde un navegador, necesitas crear una URL WebSocket prefirmada usando AWS SigV4.
El siguiente ejemplo muestra un flujo de extremo a extremo de obtención de credenciales, creación de una URL prefirmada e invocación del agente:
import { createTRPCClient, createWSClient, wsLink } from '@trpc/client';import { AwsClient } from 'aws4fetch';import { CognitoIdentityClient } from '@aws-sdk/client-cognito-identity';import { fromCognitoIdentityPool } from '@aws-sdk/credential-provider-cognito-identity';import type { AppRouter } from './your-agent/router';
// Build a presigned WebSocket URLasync function buildSignedUrl( agentRuntimeArn: string, idToken: string, region: string = 'us-west-2'): Promise<string> { // Get credentials from a Cognito Identity Pool (or other source) const credentials = fromCognitoIdentityPool({ client: new CognitoIdentityClient({ region }), identityPoolId: 'us-west-2:xxxxx', logins: { [`cognito-idp.${region}.amazonaws.com/us-west-2_xxxxx`]: idToken, }, });
const cognitoIdentity = new CognitoIdentityClient({ credentials }); const credential = await cognitoIdentity.config.credentials();
// Create AWS SigV4 client const awsClient = new AwsClient({ ...credential, service: 'bedrock-agentcore', });
// Build WebSocket URL from ARN const wsUrl = `wss://bedrock-agentcore.${region}.amazonaws.com/runtimes/${agentRuntimeArn.replace(/:/g, '%3A').replace(/\//g, '%2F')}/ws`;
// Create presigned URL const signedRequest = await awsClient.sign(wsUrl, { method: 'GET', aws: { signQuery: true }, });
return signedRequest.url;}
// Create tRPC client with presigned WebSocket URLconst agentRuntimeArn = 'arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent';const idToken = '<your-id-token>';
const wsClient = createWSClient({ url: async () => buildSignedUrl(agentRuntimeArn, idToken),});
const trpcClient = createTRPCClient<AppRouter>({ links: [wsLink({ client: wsClient })],});
// Invoke the agenttrpcClient.invoke.subscribe({ message: 'what is 1 plus 1?' }, { onData: (message) => console.log(message),});