From 91fa77f9ad9a3dcb84fe64e13f3104c6293543e2 Mon Sep 17 00:00:00 2001 From: Gunasekar-K Date: Mon, 30 Jun 2025 12:24:09 +0530 Subject: [PATCH 01/32] Circle Ci changes integration --- .circleci/config.yml | 95 ++++++++++++++++++++++++++++++++++++++++++++ build.sh | 2 +- 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..589a402 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,95 @@ +version: 2.1 +defaults: &defaults + docker: + - image: cimg/python:3.13.5-browsers +install_dependency: &install_dependency + name: Installation of build and deployment dependencies. + command: | + pip3 install awscli --upgrade +install_deploysuite: &install_deploysuite + name: Installation of install_deploysuite. + command: | + git clone --branch v1.4.17 https://github.com/topcoder-platform/tc-deploy-scripts ../buildscript + cp ./../buildscript/master_deploy.sh . + cp ./../buildscript/buildenv.sh . + cp ./../buildscript/awsconfiguration.sh . + cp ./../buildscript/psvar-processor.sh . + +restore_cache_settings_for_build: &restore_cache_settings_for_build + key: docker-node-modules-{{ checksum "pnpm-lock.yaml" }} + +save_cache_settings: &save_cache_settings + key: docker-node-modules-{{ checksum "pnpm-lock.yaml" }} + paths: + - node_modules + + +builddeploy_steps: &builddeploy_steps +- checkout +- setup_remote_docker +- run: *install_dependency +- run: *install_deploysuite +- restore_cache: *restore_cache_settings_for_build +- run: + name: "Build docker image" + command: | + ./build.sh +- save_cache: *save_cache_settings +- deploy: + name: Running MasterScript. + command: | + ./awsconfiguration.sh $DEPLOY_ENV + source awsenvconf + ./psvar-processor.sh -t appenv -p /config/${APPNAME}/deployvar + source deployvar_env + ./master_deploy.sh -d ECS -e $DEPLOY_ENV -t latest -j /config/${APPNAME}/appvar -i ${APPNAME} -p FARGATE +jobs: + # Build & Deploy against development backend + "build-dev": + !!merge <<: *defaults + environment: + DEPLOY_ENV: "DEV" + LOGICAL_ENV: "dev" + APPNAME: "tc-mcp" + steps: *builddeploy_steps + + "build-qa": + !!merge <<: *defaults + environment: + DEPLOY_ENV: "QA" + LOGICAL_ENV: "qa" + APPNAME: "tc-mcp" + steps: *builddeploy_steps + + "build-prod": + !!merge <<: *defaults + environment: + DEPLOY_ENV: "PROD" + LOGICAL_ENV: "prod" + APPNAME: "tc-mcp" + steps: *builddeploy_steps + +workflows: + version: 2 + build: + jobs: + # Development builds are executed on "develop" branch only. + - "build-dev": + context: org-global + filters: + branches: + only: + - dev + + - "build-qa": + context: org-global + filters: + branches: + only: + - qa + + - "build-prod": + context: org-global + filters: + branches: + only: master diff --git a/build.sh b/build.sh index 048a4bf..c3c0f18 100755 --- a/build.sh +++ b/build.sh @@ -1,3 +1,3 @@ #!/bin/bash set -eo pipefail -docker buildx build --no-cache=true -t ${APPNAME}}:latest . \ No newline at end of file +docker buildx build --no-cache=true -t ${APPNAME}:latest . \ No newline at end of file From a0ee9b3ee665e0b123168057479d3216d41e4a9e Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Mon, 30 Jun 2025 12:03:18 +0300 Subject: [PATCH 02/32] Add global API prefix --- src/api/health-check/healthCheck.controller.ts | 2 +- src/config/config.env.ts | 4 ++++ src/main.ts | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/api/health-check/healthCheck.controller.ts b/src/api/health-check/healthCheck.controller.ts index b47a761..4dc6266 100644 --- a/src/api/health-check/healthCheck.controller.ts +++ b/src/api/health-check/healthCheck.controller.ts @@ -19,7 +19,7 @@ export class HealthCheckController { @Public() @Version([VERSION_NEUTRAL, '1']) - @Get('/healthcheck') + @Get('/health') healthCheck(): Promise { const response = new GetHealthCheckResponseDto(); diff --git a/src/config/config.env.ts b/src/config/config.env.ts index 0130772..81a48f6 100644 --- a/src/config/config.env.ts +++ b/src/config/config.env.ts @@ -16,4 +16,8 @@ export class ConfigEnv { @IsString() AUTH0_CLIENT_ID!: string; + + @IsString() + @IsOptional() + API_BASE = '/v6/mcp'; } diff --git a/src/main.ts b/src/main.ts index c89e56d..2713bf2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,6 +8,9 @@ async function bootstrap() { const logger = new Logger('bootstrap()'); + // Global prefix for all routes + app.setGlobalPrefix(ENV_CONFIG.API_BASE); + // Add an event handler to log uncaught promise rejections and prevent the server from crashing process.on('unhandledRejection', (reason, promise) => { logger.error('Unhandled Rejection at:', promise, 'reason:', reason); From 9d503928c49669f6db00ae82c1759a2cd33d1fb2 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Tue, 1 Jul 2025 13:18:24 +0300 Subject: [PATCH 03/32] auth setup --- src/app.module.ts | 19 +------ src/core/auth/auth.constants.ts | 4 +- src/core/auth/decorators/index.ts | 1 - src/core/auth/decorators/roles.decorator.ts | 5 -- src/core/auth/guards/auth.guard.ts | 56 +++--------------- src/core/auth/guards/index.ts | 1 - src/core/auth/guards/roles.guard.ts | 57 ------------------- .../request/createRequestStore.middleware.ts | 14 ----- src/core/request/requestStore.ts | 38 ------------- src/main.ts | 4 +- .../tools/challenges/queryChallenges.tool.ts | 4 +- src/shared/global/logger.ts | 10 +--- 12 files changed, 19 insertions(+), 194 deletions(-) delete mode 100644 src/core/auth/decorators/roles.decorator.ts delete mode 100644 src/core/auth/guards/roles.guard.ts delete mode 100644 src/core/request/createRequestStore.middleware.ts delete mode 100644 src/core/request/requestStore.ts diff --git a/src/app.module.ts b/src/app.module.ts index d53eeec..228650e 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -6,9 +6,6 @@ import { GlobalProvidersModule } from './shared/global/globalProviders.module'; import { TopcoderModule } from './shared/topcoder/topcoder.module'; import { HealthCheckController } from './api/health-check/healthCheck.controller'; import { TokenValidatorMiddleware } from './core/auth/middleware/tokenValidator.middleware'; -import { CreateRequestStoreMiddleware } from './core/request/createRequestStore.middleware'; -import { AuthGuard, RolesGuard } from './core/auth/guards'; -import { APP_GUARD } from '@nestjs/core'; @Module({ imports: [ @@ -20,27 +17,15 @@ import { APP_GUARD } from '@nestjs/core'; sessionIdGenerator: () => randomUUID(), statelessMode: false, }, - // guards: [AuthGuard, RolesGuard], }), GlobalProvidersModule, TopcoderModule, ], controllers: [HealthCheckController], - providers: [ - // { - // provide: APP_GUARD, - // useClass: AuthGuard, - // }, - // { - // provide: APP_GUARD, - // useClass: RolesGuard, - // }, - QueryChallengesTool, - ], + providers: [QueryChallengesTool], }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { - // consumer.apply(TokenValidatorMiddleware).forRoutes('*'); - // consumer.apply(CreateRequestStoreMiddleware).forRoutes('*'); + consumer.apply(TokenValidatorMiddleware).forRoutes('*'); } } diff --git a/src/core/auth/auth.constants.ts b/src/core/auth/auth.constants.ts index 71de54c..5c96928 100644 --- a/src/core/auth/auth.constants.ts +++ b/src/core/auth/auth.constants.ts @@ -2,4 +2,6 @@ export enum Role { User = 'Topcoder User', } -export enum M2mScope {} +export enum M2mScope { + QueryPublicChallenges = 'query:public:challenges', +} diff --git a/src/core/auth/decorators/index.ts b/src/core/auth/decorators/index.ts index 05c68d6..07b77ea 100644 --- a/src/core/auth/decorators/index.ts +++ b/src/core/auth/decorators/index.ts @@ -1,5 +1,4 @@ export * from './m2m.decorator'; export * from './m2mScope.decorator'; export * from './public.decorator'; -export * from './roles.decorator'; export * from './user.decorator'; diff --git a/src/core/auth/decorators/roles.decorator.ts b/src/core/auth/decorators/roles.decorator.ts deleted file mode 100644 index 9ffe82c..0000000 --- a/src/core/auth/decorators/roles.decorator.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { SetMetadata } from '@nestjs/common'; -import { Role } from '../auth.constants'; - -export const ROLES_KEY = 'roles'; -export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles); diff --git a/src/core/auth/guards/auth.guard.ts b/src/core/auth/guards/auth.guard.ts index 921ae5b..b0fc624 100644 --- a/src/core/auth/guards/auth.guard.ts +++ b/src/core/auth/guards/auth.guard.ts @@ -1,59 +1,17 @@ -import { - CanActivate, - ExecutionContext, - Injectable, - UnauthorizedException, -} from '@nestjs/common'; +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; -import { IS_PUBLIC_KEY } from '../decorators/public.decorator'; -import { IS_M2M_KEY } from '../decorators/m2m.decorator'; -import { M2mScope } from '../auth.constants'; -import { SCOPES_KEY } from '../decorators/m2mScope.decorator'; +import { Logger } from 'src/shared/global'; @Injectable() export class AuthGuard implements CanActivate { + private readonly logger = new Logger(AuthGuard.name); + constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { - const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ - context.getHandler(), - context.getClass(), - ]); - - if (isPublic) return true; - - const req = context.switchToHttp().getRequest(); - const isM2M = this.reflector.getAllAndOverride(IS_M2M_KEY, [ - context.getHandler(), - context.getClass(), - ]); - - const { m2mUserId } = req; - if (m2mUserId) { - req.user = { - id: m2mUserId, - handle: '', - }; - } - - // Regular authentication - check that we have user's email and have verified the id token - if (!isM2M) { - return Boolean(req.email && req.idTokenVerified); - } - - // M2M authentication - check scopes - if (!req.idTokenVerified || !req.m2mTokenScope) - throw new UnauthorizedException(); - - const allowedM2mScopes = this.reflector.getAllAndOverride( - SCOPES_KEY, - [context.getHandler(), context.getClass()], - ); + this.logger.log('AuthGuard canActivate called...'); + // Check if the route is marked as public... - const reqScopes = req.m2mTokenScope.split(' '); - if (reqScopes.some((reqScope) => allowedM2mScopes.includes(reqScope))) { - return true; - } - return false; + return true; } } diff --git a/src/core/auth/guards/index.ts b/src/core/auth/guards/index.ts index 4ae679c..b41e34a 100644 --- a/src/core/auth/guards/index.ts +++ b/src/core/auth/guards/index.ts @@ -1,2 +1 @@ export * from './auth.guard'; -export * from './roles.guard'; diff --git a/src/core/auth/guards/roles.guard.ts b/src/core/auth/guards/roles.guard.ts deleted file mode 100644 index 8145cce..0000000 --- a/src/core/auth/guards/roles.guard.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; -import { Reflector } from '@nestjs/core'; -import { ROLES_KEY } from '../decorators/roles.decorator'; - -@Injectable() -export class RolesGuard implements CanActivate { - constructor(private reflector: Reflector) {} - - canActivate(context: ExecutionContext): boolean { - const requiredRoles = this.reflector.getAllAndOverride( - ROLES_KEY, - [context.getHandler(), context.getClass()], - ); - - if (!requiredRoles) { - return true; - } - - const request = context.switchToHttp().getRequest(); - const { auth0User } = request; - const userRoles = Object.keys(auth0User).reduce((roles, key) => { - if (key.match(/claims\/roles$/gi)) { - return auth0User[key] as string[]; - } - - return roles; - }, []); - - if (!requiredRoles.some((role) => userRoles.includes(role))) { - return false; - } - - const userHandle = Object.keys(auth0User).reduce((handles, key) => { - if (key.match(/claims\/handle$/gi)) { - return auth0User[key] as string; - } - - return handles; - }, []); - - const userId = Object.keys(auth0User).reduce((ids, key) => { - if (key.match(/claims\/userId$/gi)) { - return auth0User[key] as string; - } - - return ids; - }, []); - - request.user = { - id: userId, - handle: userHandle, - email: request.email, - }; - - return true; - } -} diff --git a/src/core/request/createRequestStore.middleware.ts b/src/core/request/createRequestStore.middleware.ts deleted file mode 100644 index 1b45180..0000000 --- a/src/core/request/createRequestStore.middleware.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Injectable, NestMiddleware } from '@nestjs/common'; -import { Response, NextFunction } from 'express'; -import { RequestMetadata, saveStore } from './requestStore'; - -@Injectable() -export class CreateRequestStoreMiddleware implements NestMiddleware { - constructor() {} - - use(req: any, res: Response, next: NextFunction) { - const requestMetaData = new RequestMetadata({}); - - saveStore(requestMetaData, next); - } -} diff --git a/src/core/request/requestStore.ts b/src/core/request/requestStore.ts deleted file mode 100644 index dc6bdd9..0000000 --- a/src/core/request/requestStore.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { AsyncLocalStorage } from 'async_hooks'; -import { NextFunction } from 'express'; -import { nanoid } from 'nanoid'; - -// Class for storing request specific metadata -export class RequestMetadata { - requestId: string; - - constructor(params: { requestId?: string }) { - this.requestId = params.requestId ?? nanoid(11); - } -} - -// Create a AsyncLocalStorage of type RequestMetaData for storing request specific data -const asyncStorage = new AsyncLocalStorage(); - -// Gets the RequestMetadada object associated with the current request -export function getStore(): RequestMetadata { - let store = asyncStorage.getStore(); - if (store === undefined) { - store = new RequestMetadata({ - requestId: '', - }); - } - - return store; -} - -// For use in middleware -// Saves RequestMetadata for the current request -export function saveStore( - requestMetaData: RequestMetadata, - next: NextFunction, -) { - asyncStorage.run(requestMetaData, () => { - next(); - }); -} diff --git a/src/main.ts b/src/main.ts index 2713bf2..f50fe2a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,9 @@ import { Logger } from 'src/shared/global'; import { ENV_CONFIG } from './config'; async function bootstrap() { - const app = await NestFactory.create(AppModule); + const app = await NestFactory.create(AppModule, { + logger: ['error', 'warn', 'log'], + }); const logger = new Logger('bootstrap()'); diff --git a/src/mcp/tools/challenges/queryChallenges.tool.ts b/src/mcp/tools/challenges/queryChallenges.tool.ts index 345c9e6..4ff6dce 100644 --- a/src/mcp/tools/challenges/queryChallenges.tool.ts +++ b/src/mcp/tools/challenges/queryChallenges.tool.ts @@ -1,11 +1,12 @@ // greeting.tool.ts -import { Injectable, Inject } from '@nestjs/common'; +import { Injectable, Inject, UseGuards } from '@nestjs/common'; import { Tool } from '@rekog/mcp-nest'; import { REQUEST } from '@nestjs/core'; import { QUERY_CHALLENGES_TOOL_PARAMETERS } from './queryChallenges.parameters'; import { TopcoderChallengesService } from 'src/shared/topcoder/challenges.service'; import { Logger } from 'src/shared/global'; import { QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA } from './queryChallenges.output'; +import { AuthGuard } from 'src/core/auth/guards'; @Injectable() export class QueryChallengesTool { @@ -27,6 +28,7 @@ export class QueryChallengesTool { readOnlyHint: true, }, }) + @UseGuards(AuthGuard) async queryChallenges(params) { // Validate the input parameters const validatedParams = QUERY_CHALLENGES_TOOL_PARAMETERS.safeParse(params); diff --git a/src/shared/global/logger.ts b/src/shared/global/logger.ts index a6c6bb8..93142b7 100644 --- a/src/shared/global/logger.ts +++ b/src/shared/global/logger.ts @@ -1,12 +1,7 @@ import { Logger as NestLogger } from '@nestjs/common'; import * as stringify from 'json-stringify-safe'; -import { getStore } from 'src/core/request/requestStore'; export class Logger extends NestLogger { - private get store() { - return getStore(); - } - log(...messages: any[]): void { super.log(this.formatMessages(messages)); } @@ -24,10 +19,7 @@ export class Logger extends NestLogger { } private formatMessages(messages: any[]): string { - const requestIdPrefix = this.store.requestId - ? [`{${this.store.requestId}}`] - : []; - return [...requestIdPrefix, ...messages] + return [...messages] .map((msg) => // eslint-disable-next-line @typescript-eslint/no-unsafe-return typeof msg === 'object' ? stringify(msg, null, 2) : String(msg), From 31b559120f18a96029a2292fbcf4fc6195d9a45f Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Tue, 1 Jul 2025 18:31:33 +0300 Subject: [PATCH 04/32] form mcp-nest --- package.json | 2 +- pnpm-lock.yaml | 13 +++++++------ src/app.module.ts | 6 +++--- src/mcp/tools/challenges/queryChallenges.tool.ts | 3 +-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 7f09332..a899e47 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "@nestjs/common": "^11.0.1", "@nestjs/core": "^11.0.1", "@nestjs/platform-express": "^11.0.1", - "@rekog/mcp-nest": "^1.6.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", "dotenv": "^16.5.0", @@ -34,6 +33,7 @@ "nanoid": "^5.1.5", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", + "@tc/mcp-nest": "topcoder-platform/MCP-Nest.git", "zod": "^3.25.67" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0f0d218..6699b3f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,9 +20,9 @@ importers: '@nestjs/platform-express': specifier: ^11.0.1 version: 11.1.3(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3) - '@rekog/mcp-nest': - specifier: ^1.6.2 - version: 1.6.2(@modelcontextprotocol/sdk@1.13.0)(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(express@5.1.0)(reflect-metadata@0.2.2)(zod-to-json-schema@3.24.5(zod@3.25.67))(zod@3.25.67) + '@tc/mcp-nest': + specifier: topcoder-platform/MCP-Nest.git + version: '@rekog/mcp-nest@https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/8950083c999f7636fed9f62b26330748aed9ed30(@modelcontextprotocol/sdk@1.13.0)(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(express@5.1.0)(reflect-metadata@0.2.2)(zod-to-json-schema@3.24.5(zod@3.25.67))(zod@3.25.67)' class-transformer: specifier: ^0.5.1 version: 0.5.1 @@ -842,8 +842,9 @@ packages: resolution: {integrity: sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@rekog/mcp-nest@1.6.2': - resolution: {integrity: sha512-swcu99a/woQ5T1f4E/YJct1w/xzszH83bhzEuZwXh+cYRnQUJKhaJd5Aey/XyOtt/D9lQTKSdnoKdpija8kuWA==} + '@rekog/mcp-nest@https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/8950083c999f7636fed9f62b26330748aed9ed30': + resolution: {tarball: https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/8950083c999f7636fed9f62b26330748aed9ed30} + version: 1.6.2 peerDependencies: '@modelcontextprotocol/sdk': '>=1.10.0' '@nestjs/common': '>=9.0.0' @@ -4386,7 +4387,7 @@ snapshots: '@pkgr/core@0.2.7': {} - '@rekog/mcp-nest@1.6.2(@modelcontextprotocol/sdk@1.13.0)(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(express@5.1.0)(reflect-metadata@0.2.2)(zod-to-json-schema@3.24.5(zod@3.25.67))(zod@3.25.67)': + '@rekog/mcp-nest@https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/8950083c999f7636fed9f62b26330748aed9ed30(@modelcontextprotocol/sdk@1.13.0)(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(express@5.1.0)(reflect-metadata@0.2.2)(zod-to-json-schema@3.24.5(zod@3.25.67))(zod@3.25.67)': dependencies: '@modelcontextprotocol/sdk': 1.13.0 '@nestjs/common': 11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) diff --git a/src/app.module.ts b/src/app.module.ts index 228650e..f5c9db1 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,11 +1,11 @@ import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; -import { McpModule } from '@rekog/mcp-nest'; +import { McpModule } from '@tc/mcp-nest'; import { QueryChallengesTool } from './mcp/tools/challenges/queryChallenges.tool'; -import { randomUUID } from 'crypto'; import { GlobalProvidersModule } from './shared/global/globalProviders.module'; import { TopcoderModule } from './shared/topcoder/topcoder.module'; import { HealthCheckController } from './api/health-check/healthCheck.controller'; import { TokenValidatorMiddleware } from './core/auth/middleware/tokenValidator.middleware'; +import { nanoid } from 'nanoid'; @Module({ imports: [ @@ -14,7 +14,7 @@ import { TokenValidatorMiddleware } from './core/auth/middleware/tokenValidator. version: '1.0.0', streamableHttp: { enableJsonResponse: false, - sessionIdGenerator: () => randomUUID(), + sessionIdGenerator: () => nanoid(), statelessMode: false, }, }), diff --git a/src/mcp/tools/challenges/queryChallenges.tool.ts b/src/mcp/tools/challenges/queryChallenges.tool.ts index 4ff6dce..328556f 100644 --- a/src/mcp/tools/challenges/queryChallenges.tool.ts +++ b/src/mcp/tools/challenges/queryChallenges.tool.ts @@ -1,6 +1,5 @@ -// greeting.tool.ts import { Injectable, Inject, UseGuards } from '@nestjs/common'; -import { Tool } from '@rekog/mcp-nest'; +import { Tool } from '@tc/mcp-nest'; import { REQUEST } from '@nestjs/core'; import { QUERY_CHALLENGES_TOOL_PARAMETERS } from './queryChallenges.parameters'; import { TopcoderChallengesService } from 'src/shared/topcoder/challenges.service'; From 5f404f0b03be2ef29edc5e78967a471e8a51251d Mon Sep 17 00:00:00 2001 From: Vasilica Olariu Date: Thu, 3 Jul 2025 23:13:22 +0300 Subject: [PATCH 05/32] Use canActivate for tools --- src/core/auth/auth.constants.ts | 1 + src/core/auth/guards/auth.guard.ts | 23 ++--- src/core/auth/guards/guards.utils.ts | 27 ++++++ src/core/auth/guards/index.ts | 2 + src/core/auth/guards/m2m-scope.guard.ts | 19 ++++ src/core/auth/guards/role.guard.ts | 23 +++++ .../tools/challenges/queryChallenges.tool.ts | 86 +++++++++++++++---- 7 files changed, 151 insertions(+), 30 deletions(-) create mode 100644 src/core/auth/guards/guards.utils.ts create mode 100644 src/core/auth/guards/m2m-scope.guard.ts create mode 100644 src/core/auth/guards/role.guard.ts diff --git a/src/core/auth/auth.constants.ts b/src/core/auth/auth.constants.ts index 5c96928..e0d1620 100644 --- a/src/core/auth/auth.constants.ts +++ b/src/core/auth/auth.constants.ts @@ -1,4 +1,5 @@ export enum Role { + Admin = 'administrator', User = 'Topcoder User', } diff --git a/src/core/auth/guards/auth.guard.ts b/src/core/auth/guards/auth.guard.ts index b0fc624..a04c150 100644 --- a/src/core/auth/guards/auth.guard.ts +++ b/src/core/auth/guards/auth.guard.ts @@ -1,17 +1,10 @@ -import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; -import { Reflector } from '@nestjs/core'; -import { Logger } from 'src/shared/global'; +import { Request } from 'express'; +import { decode } from './guards.utils'; -@Injectable() -export class AuthGuard implements CanActivate { - private readonly logger = new Logger(AuthGuard.name); - - constructor(private reflector: Reflector) {} - - canActivate(context: ExecutionContext): boolean { - this.logger.log('AuthGuard canActivate called...'); - // Check if the route is marked as public... - - return true; +export const authGuard = async (req: Request) => { + if (!(await decode(req.headers.authorization ?? ''))) { + return false; } -} + + return true; +}; diff --git a/src/core/auth/guards/guards.utils.ts b/src/core/auth/guards/guards.utils.ts new file mode 100644 index 0000000..10eba0d --- /dev/null +++ b/src/core/auth/guards/guards.utils.ts @@ -0,0 +1,27 @@ +import * as jwt from 'jsonwebtoken'; +// import { UnauthorizedException } from '@nestjs/common'; +import { Logger } from 'src/shared/global'; +import { getSigningKey } from '../jwt'; + +const logger = new Logger('guards.utils()'); + +export const decode = async (authHeader: string) => { + const [type, idToken] = authHeader?.split(' ') ?? []; + + if (type !== 'Bearer' || !idToken) { + return false; + // throw new UnauthorizedException('Missing Authorization header!'); + } + + let decoded: jwt.JwtPayload | string; + try { + const signingKey = await getSigningKey(idToken); + decoded = jwt.verify(idToken, signingKey); + } catch (error) { + logger.error('Error verifying JWT', error); + return false; + // throw new UnauthorizedException('Invalid or expired JWT!'); + } + + return decoded; +}; diff --git a/src/core/auth/guards/index.ts b/src/core/auth/guards/index.ts index b41e34a..49afb20 100644 --- a/src/core/auth/guards/index.ts +++ b/src/core/auth/guards/index.ts @@ -1 +1,3 @@ export * from './auth.guard'; +export * from './m2m-scope.guard'; +export * from './role.guard'; diff --git a/src/core/auth/guards/m2m-scope.guard.ts b/src/core/auth/guards/m2m-scope.guard.ts new file mode 100644 index 0000000..f575e2f --- /dev/null +++ b/src/core/auth/guards/m2m-scope.guard.ts @@ -0,0 +1,19 @@ +import { Request } from 'express'; +import { decode } from './guards.utils'; +import { JwtPayload } from 'jsonwebtoken'; +import { M2mScope } from '../auth.constants'; + +export const checkM2MScope = + (...requiredM2mScopes: M2mScope[]) => + async (req: Request) => { + const decodedAuth = await decode(req.headers.authorization ?? ''); + + const authorizedScopes = ((decodedAuth as JwtPayload).scope ?? '').split( + ' ', + ); + if (!requiredM2mScopes.some((scope) => authorizedScopes.includes(scope))) { + return false; + } + + return true; + }; diff --git a/src/core/auth/guards/role.guard.ts b/src/core/auth/guards/role.guard.ts new file mode 100644 index 0000000..f1d5329 --- /dev/null +++ b/src/core/auth/guards/role.guard.ts @@ -0,0 +1,23 @@ +import { Request } from 'express'; +import { decode } from './guards.utils'; +import { Role } from '../auth.constants'; + +export const checkHasUserRole = + (...requiredUserRoles: Role[]) => + async (req: Request) => { + const decodedAuth = await decode(req.headers.authorization ?? ''); + + const decodedUserRoles = Object.keys(decodedAuth).reduce((roles, key) => { + if (key.match(/claims\/roles$/gi)) { + return decodedAuth[key] as string[]; + } + + return roles; + }, []); + + if (!requiredUserRoles.some((role) => decodedUserRoles.includes(role))) { + return false; + } + + return true; + }; diff --git a/src/mcp/tools/challenges/queryChallenges.tool.ts b/src/mcp/tools/challenges/queryChallenges.tool.ts index 328556f..ed55795 100644 --- a/src/mcp/tools/challenges/queryChallenges.tool.ts +++ b/src/mcp/tools/challenges/queryChallenges.tool.ts @@ -1,11 +1,16 @@ -import { Injectable, Inject, UseGuards } from '@nestjs/common'; +import { Injectable, Inject } from '@nestjs/common'; import { Tool } from '@tc/mcp-nest'; import { REQUEST } from '@nestjs/core'; import { QUERY_CHALLENGES_TOOL_PARAMETERS } from './queryChallenges.parameters'; import { TopcoderChallengesService } from 'src/shared/topcoder/challenges.service'; import { Logger } from 'src/shared/global'; import { QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA } from './queryChallenges.output'; -import { AuthGuard } from 'src/core/auth/guards'; +import { + authGuard, + checkHasUserRole, + checkM2MScope, +} from 'src/core/auth/guards'; +import { M2mScope, Role } from 'src/core/auth/auth.constants'; @Injectable() export class QueryChallengesTool { @@ -16,19 +21,7 @@ export class QueryChallengesTool { @Inject(REQUEST) private readonly request: any, ) {} - @Tool({ - name: 'query-tc-challenges', - description: - 'Returns a list of public Topcoder challenges based on the query parameters.', - parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, - outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, - annotations: { - title: 'Query Public Topcoder Challenges', - readOnlyHint: true, - }, - }) - @UseGuards(AuthGuard) - async queryChallenges(params) { + private async _queryChallenges(params) { // Validate the input parameters const validatedParams = QUERY_CHALLENGES_TOOL_PARAMETERS.safeParse(params); if (!validatedParams.success) { @@ -127,4 +120,67 @@ export class QueryChallengesTool { }; } } + + @Tool({ + name: 'query-tc-challenges-private', + description: + 'Returns a list of public Topcoder challenges based on the query parameters.', + parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, + outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, + annotations: { + title: 'Query Public Topcoder Challenges', + readOnlyHint: true, + }, + canActivate: authGuard, + }) + async queryChallengesPrivate(params) { + return this._queryChallenges(params); + } + + @Tool({ + name: 'query-tc-challenges-protected', + description: + 'Returns a list of public Topcoder challenges based on the query parameters.', + parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, + outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, + annotations: { + title: 'Query Public Topcoder Challenges', + readOnlyHint: true, + }, + canActivate: checkHasUserRole(Role.Admin), + }) + async queryChallengesProtected(params) { + return this._queryChallenges(params); + } + + @Tool({ + name: 'query-tc-challenges-m2m', + description: + 'Returns a list of public Topcoder challenges based on the query parameters.', + parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, + outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, + annotations: { + title: 'Query Public Topcoder Challenges', + readOnlyHint: true, + }, + canActivate: checkM2MScope(M2mScope.QueryPublicChallenges), + }) + async queryChallengesM2m(params) { + return this._queryChallenges(params); + } + + @Tool({ + name: 'query-tc-challenges-public', + description: + 'Returns a list of public Topcoder challenges based on the query parameters.', + parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, + outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, + annotations: { + title: 'Query Public Topcoder Challenges', + readOnlyHint: true, + }, + }) + async queryChallengesPublic(params) { + return this._queryChallenges(params); + } } From a2f1683859ec7b54cb494fc40d22fb4554d2e8b8 Mon Sep 17 00:00:00 2001 From: Vasilica Olariu Date: Fri, 4 Jul 2025 15:30:52 +0300 Subject: [PATCH 06/32] rename method & add doc comments --- src/core/auth/guards/auth.guard.ts | 10 ++++++++-- src/core/auth/guards/guards.utils.ts | 16 ++++++++++++---- src/core/auth/guards/m2m-scope.guard.ts | 15 +++++++++++++-- src/core/auth/guards/role.guard.ts | 15 +++++++++++++-- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/core/auth/guards/auth.guard.ts b/src/core/auth/guards/auth.guard.ts index a04c150..a63de76 100644 --- a/src/core/auth/guards/auth.guard.ts +++ b/src/core/auth/guards/auth.guard.ts @@ -1,8 +1,14 @@ import { Request } from 'express'; -import { decode } from './guards.utils'; +import { decodeAuthToken } from './guards.utils'; +/** + * Auth guard function to validate the authorization token from the request headers. + * + * @param req - The incoming HTTP request object. + * @returns A promise that resolves to `true` if the authorization token is valid, otherwise `false`. + */ export const authGuard = async (req: Request) => { - if (!(await decode(req.headers.authorization ?? ''))) { + if (!(await decodeAuthToken(req.headers.authorization ?? ''))) { return false; } diff --git a/src/core/auth/guards/guards.utils.ts b/src/core/auth/guards/guards.utils.ts index 10eba0d..cd83d82 100644 --- a/src/core/auth/guards/guards.utils.ts +++ b/src/core/auth/guards/guards.utils.ts @@ -1,16 +1,25 @@ import * as jwt from 'jsonwebtoken'; -// import { UnauthorizedException } from '@nestjs/common'; import { Logger } from 'src/shared/global'; import { getSigningKey } from '../jwt'; const logger = new Logger('guards.utils()'); -export const decode = async (authHeader: string) => { +/** + * Decodes and verifies a JWT token from the provided authorization header. + * + * @param authHeader - The authorization header containing the token, expected in the format "Bearer ". + * @returns A promise that resolves to the decoded JWT payload if the token is valid, + * a string if the payload is a string, or `false` if the token is invalid or the header is improperly formatted. + * + * @throws This function does not throw directly but will return `false` if an error occurs during verification. + */ +export const decodeAuthToken = async ( + authHeader: string, +): Promise => { const [type, idToken] = authHeader?.split(' ') ?? []; if (type !== 'Bearer' || !idToken) { return false; - // throw new UnauthorizedException('Missing Authorization header!'); } let decoded: jwt.JwtPayload | string; @@ -20,7 +29,6 @@ export const decode = async (authHeader: string) => { } catch (error) { logger.error('Error verifying JWT', error); return false; - // throw new UnauthorizedException('Invalid or expired JWT!'); } return decoded; diff --git a/src/core/auth/guards/m2m-scope.guard.ts b/src/core/auth/guards/m2m-scope.guard.ts index f575e2f..b1d85a5 100644 --- a/src/core/auth/guards/m2m-scope.guard.ts +++ b/src/core/auth/guards/m2m-scope.guard.ts @@ -1,12 +1,23 @@ import { Request } from 'express'; -import { decode } from './guards.utils'; +import { decodeAuthToken } from './guards.utils'; import { JwtPayload } from 'jsonwebtoken'; import { M2mScope } from '../auth.constants'; +/** + * A utility function to check if the required M2M (Machine-to-Machine) scopes are present + * in the authorization token provided in the request headers. + * + * @param {...M2mScope[]} requiredM2mScopes - The list of required M2M scopes to validate against. + * @returns {Promise<(req: Request) => boolean>} A function that takes an Express `Request` object + * and returns a boolean indicating whether the required scopes are present. + * + * The function decodes the authorization token from the request headers and checks if + * the required scopes are included in the token's scope claim. + */ export const checkM2MScope = (...requiredM2mScopes: M2mScope[]) => async (req: Request) => { - const decodedAuth = await decode(req.headers.authorization ?? ''); + const decodedAuth = await decodeAuthToken(req.headers.authorization ?? ''); const authorizedScopes = ((decodedAuth as JwtPayload).scope ?? '').split( ' ', diff --git a/src/core/auth/guards/role.guard.ts b/src/core/auth/guards/role.guard.ts index f1d5329..15fbc50 100644 --- a/src/core/auth/guards/role.guard.ts +++ b/src/core/auth/guards/role.guard.ts @@ -1,11 +1,22 @@ import { Request } from 'express'; -import { decode } from './guards.utils'; +import { decodeAuthToken } from './guards.utils'; import { Role } from '../auth.constants'; +/** + * A utility function to check if the required user role are present + * in the authorization token provided in the request headers. + * + * @param {...Role[]} requiredUserRoles - The list of required user roles to validate against. + * @returns {Promise<(req: Request) => boolean>} A function that takes an Express `Request` object + * and returns a boolean indicating whether the required scopes are present. + * + * The function decodes the authorization token from the request headers and checks if + * the required user roles are included in the token's scope claim. + */ export const checkHasUserRole = (...requiredUserRoles: Role[]) => async (req: Request) => { - const decodedAuth = await decode(req.headers.authorization ?? ''); + const decodedAuth = await decodeAuthToken(req.headers.authorization ?? ''); const decodedUserRoles = Object.keys(decodedAuth).reduce((roles, key) => { if (key.match(/claims\/roles$/gi)) { From 7c26fea893a6302b4e4d10b4d2dfd1dd6d76db00 Mon Sep 17 00:00:00 2001 From: Vasilica Olariu Date: Thu, 10 Jul 2025 10:16:21 +0300 Subject: [PATCH 07/32] update pnpm-lock --- pnpm-lock.yaml | 1133 ++++++++++++++++++++++++------------------------ 1 file changed, 563 insertions(+), 570 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6699b3f..c45a4f1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@modelcontextprotocol/sdk': specifier: ^1.13.0 - version: 1.13.0 + version: 1.13.3 '@nestjs/common': specifier: ^11.0.1 version: 11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -22,7 +22,7 @@ importers: version: 11.1.3(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3) '@tc/mcp-nest': specifier: topcoder-platform/MCP-Nest.git - version: '@rekog/mcp-nest@https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/8950083c999f7636fed9f62b26330748aed9ed30(@modelcontextprotocol/sdk@1.13.0)(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(express@5.1.0)(reflect-metadata@0.2.2)(zod-to-json-schema@3.24.5(zod@3.25.67))(zod@3.25.67)' + version: https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/025395f5f3a665e8439e481f84c25c6e6ed97933(@modelcontextprotocol/sdk@1.13.3)(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(express@5.1.0)(reflect-metadata@0.2.2)(zod-to-json-schema@3.24.6(zod@3.25.71))(zod@3.25.71) class-transformer: specifier: ^0.5.1 version: 0.5.1 @@ -31,7 +31,7 @@ importers: version: 0.14.2 dotenv: specifier: ^16.5.0 - version: 16.5.0 + version: 16.6.1 json-stringify-safe: specifier: ^5.0.1 version: 5.0.1 @@ -52,17 +52,17 @@ importers: version: 7.8.2 zod: specifier: ^3.25.67 - version: 3.25.67 + version: 3.25.71 devDependencies: '@eslint/eslintrc': specifier: ^3.2.0 version: 3.3.1 '@eslint/js': specifier: ^9.18.0 - version: 9.29.0 + version: 9.30.1 '@nestjs/cli': specifier: ^11.0.0 - version: 11.0.7(@swc/cli@0.6.0(@swc/core@1.12.3)(chokidar@4.0.3))(@swc/core@1.12.3)(@types/node@22.15.32) + version: 11.0.7(@swc/cli@0.6.0(@swc/core@1.12.9)(chokidar@4.0.3))(@swc/core@1.12.9)(@types/node@22.16.0) '@nestjs/schematics': specifier: ^11.0.0 version: 11.0.5(chokidar@4.0.3)(typescript@5.8.3) @@ -71,10 +71,10 @@ importers: version: 11.1.3(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(@nestjs/platform-express@11.1.3) '@swc/cli': specifier: ^0.6.0 - version: 0.6.0(@swc/core@1.12.3)(chokidar@4.0.3) + version: 0.6.0(@swc/core@1.12.9)(chokidar@4.0.3) '@swc/core': specifier: ^1.10.7 - version: 1.12.3 + version: 1.12.9 '@types/express': specifier: ^5.0.0 version: 5.0.3 @@ -83,28 +83,28 @@ importers: version: 29.5.14 '@types/node': specifier: ^22.15.32 - version: 22.15.32 + version: 22.16.0 '@types/supertest': specifier: ^6.0.2 version: 6.0.3 eslint: specifier: ^9.18.0 - version: 9.29.0 + version: 9.30.1 eslint-config-prettier: specifier: ^10.0.1 - version: 10.1.5(eslint@9.29.0) + version: 10.1.5(eslint@9.30.1) eslint-plugin-prettier: specifier: ^5.2.2 - version: 5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@10.1.5(eslint@9.29.0))(eslint@9.29.0)(prettier@3.5.3) + version: 5.5.1(@types/eslint@9.6.1)(eslint-config-prettier@10.1.5(eslint@9.30.1))(eslint@9.30.1)(prettier@3.6.2) globals: specifier: ^15.14.0 version: 15.15.0 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)) + version: 29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)) prettier: specifier: ^3.4.2 - version: 3.5.3 + version: 3.6.2 source-map-support: specifier: ^0.5.21 version: 0.5.21 @@ -113,13 +113,13 @@ importers: version: 7.1.1 ts-jest: specifier: ^29.2.5 - version: 29.4.0(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)))(typescript@5.8.3) + version: 29.4.0(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)))(typescript@5.8.3) ts-loader: specifier: ^9.5.2 - version: 9.5.2(typescript@5.8.3)(webpack@5.99.6(@swc/core@1.12.3)) + version: 9.5.2(typescript@5.8.3)(webpack@5.99.6(@swc/core@1.12.9)) ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3) + version: 10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3) tsconfig-paths: specifier: ^4.2.0 version: 4.2.0 @@ -128,7 +128,7 @@ importers: version: 5.8.3 typescript-eslint: specifier: ^8.20.0 - version: 8.34.1(eslint@9.29.0)(typescript@5.8.3) + version: 8.35.1(eslint@9.30.1)(typescript@5.8.3) packages: @@ -171,22 +171,26 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.27.5': - resolution: {integrity: sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==} + '@babel/compat-data@7.28.0': + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} engines: {node: '>=6.9.0'} - '@babel/core@7.27.4': - resolution: {integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==} + '@babel/core@7.28.0': + resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} engines: {node: '>=6.9.0'} - '@babel/generator@7.27.5': - resolution: {integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==} + '@babel/generator@7.28.0': + resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.27.1': resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} @@ -217,8 +221,8 @@ packages: resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.5': - resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} engines: {node: '>=6.0.0'} hasBin: true @@ -317,12 +321,12 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.27.4': - resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==} + '@babel/traverse@7.28.0': + resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.6': - resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} + '@babel/types@7.28.0': + resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': @@ -346,36 +350,36 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.20.1': - resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.2.3': - resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} + '@eslint/config-helpers@0.3.0': + resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/core@0.14.0': resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.15.0': - resolution: {integrity: sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==} + '@eslint/core@0.15.1': + resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.29.0': - resolution: {integrity: sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==} + '@eslint/js@9.30.1': + resolution: {integrity: sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.2': - resolution: {integrity: sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==} + '@eslint/plugin-kit@0.3.3': + resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@humanfs/core@0.19.1': @@ -398,8 +402,8 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@inquirer/checkbox@4.1.8': - resolution: {integrity: sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==} + '@inquirer/checkbox@4.1.9': + resolution: {integrity: sha512-DBJBkzI5Wx4jFaYm221LHvAhpKYkhVS0k9plqHwaHhofGNxvYB7J3Bz8w+bFJ05zaMb0sZNHo4KdmENQFlNTuQ==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -407,8 +411,8 @@ packages: '@types/node': optional: true - '@inquirer/confirm@5.1.12': - resolution: {integrity: sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==} + '@inquirer/confirm@5.1.13': + resolution: {integrity: sha512-EkCtvp67ICIVVzjsquUiVSd+V5HRGOGQfsqA4E4vMWhYnB7InUL0pa0TIWt1i+OfP16Gkds8CdIu6yGZwOM1Yw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -416,8 +420,8 @@ packages: '@types/node': optional: true - '@inquirer/core@10.1.13': - resolution: {integrity: sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==} + '@inquirer/core@10.1.14': + resolution: {integrity: sha512-Ma+ZpOJPewtIYl6HZHZckeX1STvDnHTCB2GVINNUlSEn2Am6LddWwfPkIGY0IUFVjUUrr/93XlBwTK6mfLjf0A==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -425,8 +429,8 @@ packages: '@types/node': optional: true - '@inquirer/editor@4.2.13': - resolution: {integrity: sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==} + '@inquirer/editor@4.2.14': + resolution: {integrity: sha512-yd2qtLl4QIIax9DTMZ1ZN2pFrrj+yL3kgIWxm34SS6uwCr0sIhsNyudUjAo5q3TqI03xx4SEBkUJqZuAInp9uA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -434,8 +438,8 @@ packages: '@types/node': optional: true - '@inquirer/expand@4.0.15': - resolution: {integrity: sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==} + '@inquirer/expand@4.0.16': + resolution: {integrity: sha512-oiDqafWzMtofeJyyGkb1CTPaxUkjIcSxePHHQCfif8t3HV9pHcw1Kgdw3/uGpDvaFfeTluwQtWiqzPVjAqS3zA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -447,8 +451,8 @@ packages: resolution: {integrity: sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==} engines: {node: '>=18'} - '@inquirer/input@4.1.12': - resolution: {integrity: sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==} + '@inquirer/input@4.2.0': + resolution: {integrity: sha512-opqpHPB1NjAmDISi3uvZOTrjEEU5CWVu/HBkDby8t93+6UxYX0Z7Ps0Ltjm5sZiEbWenjubwUkivAEYQmy9xHw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -456,8 +460,8 @@ packages: '@types/node': optional: true - '@inquirer/number@3.0.15': - resolution: {integrity: sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==} + '@inquirer/number@3.0.16': + resolution: {integrity: sha512-kMrXAaKGavBEoBYUCgualbwA9jWUx2TjMA46ek+pEKy38+LFpL9QHlTd8PO2kWPUgI/KB+qi02o4y2rwXbzr3Q==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -465,8 +469,8 @@ packages: '@types/node': optional: true - '@inquirer/password@4.0.15': - resolution: {integrity: sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==} + '@inquirer/password@4.0.16': + resolution: {integrity: sha512-g8BVNBj5Zeb5/Y3cSN+hDUL7CsIFDIuVxb9EPty3lkxBaYpjL5BNRKSYOF9yOLe+JOcKFd+TSVeADQ4iSY7rbg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -492,8 +496,8 @@ packages: '@types/node': optional: true - '@inquirer/rawlist@4.1.3': - resolution: {integrity: sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==} + '@inquirer/rawlist@4.1.4': + resolution: {integrity: sha512-5GGvxVpXXMmfZNtvWw4IsHpR7RzqAR624xtkPd1NxxlV5M+pShMqzL4oRddRkg8rVEOK9fKdJp1jjVML2Lr7TQ==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -501,8 +505,8 @@ packages: '@types/node': optional: true - '@inquirer/search@3.0.15': - resolution: {integrity: sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==} + '@inquirer/search@3.0.16': + resolution: {integrity: sha512-POCmXo+j97kTGU6aeRjsPyuCpQQfKcMXdeTMw708ZMtWrj5aykZvlUxH4Qgz3+Y1L/cAVZsSpA+UgZCu2GMOMg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -510,8 +514,8 @@ packages: '@types/node': optional: true - '@inquirer/select@4.2.3': - resolution: {integrity: sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg==} + '@inquirer/select@4.2.4': + resolution: {integrity: sha512-unTppUcTjmnbl/q+h8XeQDhAqIOmwWYWNyiiP2e3orXrg6tOaa5DHXja9PChCSbChOsktyKgOieRZFnajzxoBg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -614,26 +618,21 @@ packages: resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.12': + resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - - '@jridgewell/source-map@0.3.6': - resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + '@jridgewell/source-map@0.3.10': + resolution: {integrity: sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==} - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.29': + resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -642,8 +641,8 @@ packages: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} - '@modelcontextprotocol/sdk@1.13.0': - resolution: {integrity: sha512-P5FZsXU0kY881F6Hbk9GhsYx02/KgWK1DYf7/tyE/1lcFKhDYPQR9iYjhQXJn+Sg6hQleMo3DB7h7+p4wgp2Lw==} + '@modelcontextprotocol/sdk@1.13.3': + resolution: {integrity: sha512-bGwA78F/U5G2jrnsdRkPY3IwIwZeWUEfb5o764b79lb0rJmMT76TLwKhdNZOWakOQtedYefwIR4emisEMvInKA==} engines: {node: '>=18'} '@napi-rs/nice-android-arm-eabi@1.0.1': @@ -842,18 +841,6 @@ packages: resolution: {integrity: sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@rekog/mcp-nest@https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/8950083c999f7636fed9f62b26330748aed9ed30': - resolution: {tarball: https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/8950083c999f7636fed9f62b26330748aed9ed30} - version: 1.6.2 - peerDependencies: - '@modelcontextprotocol/sdk': '>=1.10.0' - '@nestjs/common': '>=9.0.0' - '@nestjs/core': '>=9.0.0' - express: '>=4.0.0' - reflect-metadata: '>=0.1.14' - zod: '>=3.0.0' - zod-to-json-schema: '>=3.23.0' - '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -881,68 +868,68 @@ packages: chokidar: optional: true - '@swc/core-darwin-arm64@1.12.3': - resolution: {integrity: sha512-QCV9vQ/s27AMxm8j8MTDL/nDoiEMrANiENRrWnb0Fxvz/O39CajPVShp/W7HlOkzt1GYtUXPdQJpSKylugfrWw==} + '@swc/core-darwin-arm64@1.12.9': + resolution: {integrity: sha512-GACFEp4nD6V+TZNR2JwbMZRHB+Yyvp14FrcmB6UCUYmhuNWjkxi+CLnEvdbuiKyQYv0zA+TRpCHZ+whEs6gwfA==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.12.3': - resolution: {integrity: sha512-LylCMfzGhdvl5tyKaTT9ePetHUX7wSsST7hxWiHzS+cUMj7FnhcfdEr6kcNVT7y1RJn3fCvuv7T98ZB+T2q3HA==} + '@swc/core-darwin-x64@1.12.9': + resolution: {integrity: sha512-hv2kls7Ilkm2EpeJz+I9MCil7pGS3z55ZAgZfxklEuYsxpICycxeH+RNRv4EraggN44ms+FWCjtZFu0LGg2V3g==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.12.3': - resolution: {integrity: sha512-DQODb7S+q+pwQY41Azcavwb2rb4rGxP70niScRDxB9X68hHOM9D0w9fxzC+Nr3AHcPSmVJUYUIiq5h38O5hVgQ==} + '@swc/core-linux-arm-gnueabihf@1.12.9': + resolution: {integrity: sha512-od9tDPiG+wMU9wKtd6y3nYJdNqgDOyLdgRRcrj1/hrbHoUPOM8wZQZdwQYGarw63iLXGgsw7t5HAF9Yc51ilFA==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.12.3': - resolution: {integrity: sha512-nTxtJSq78AjeaQBueYImoFBs5j7qXbgOxtirpyt8jE29NQBd0VFzDzRBhkr6I9jq0hNiChgMkqBN4eUkEQjytg==} + '@swc/core-linux-arm64-gnu@1.12.9': + resolution: {integrity: sha512-6qx1ka9LHcLzxIgn2Mros+CZLkHK2TawlXzi/h7DJeNnzi8F1Hw0Yzjp8WimxNCg6s2n+o3jnmin1oXB7gg8rw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.12.3': - resolution: {integrity: sha512-lBGvC5UgPSxqLr/y1NZxQhyRQ7nXy3/Ec1Z47YNXtqtpKiG1EcOGPyS0UZgwiYQkXqq8NBFMHnyHmpKnXTvRDA==} + '@swc/core-linux-arm64-musl@1.12.9': + resolution: {integrity: sha512-yghFZWKPVVGbUdqiD7ft23G0JX6YFGDJPz9YbLLAwGuKZ9th3/jlWoQDAw1Naci31LQhVC+oIji6ozihSuwB2A==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.12.3': - resolution: {integrity: sha512-61wZ8hwxNYzBY9MCWB50v90ICzdIhOuPk1O1qXswz9AXw5O6iQStEBHQ1rozPkfQ/rmhepk0pOf/6LCwssJOwg==} + '@swc/core-linux-x64-gnu@1.12.9': + resolution: {integrity: sha512-SFUxyhWLZRNL8QmgGNqdi2Q43PNyFVkRZ2zIif30SOGFSxnxcf2JNeSeBgKIGVgaLSuk6xFVVCtJ3KIeaStgRg==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.12.3': - resolution: {integrity: sha512-NNeBiTpCgWt80vumTKVoaj6Fa/ZjUcaNQNM7np3PIgB8EbuXfyztboV7vUxpkmD/lUgsk8GlEFYViHvo6VMefQ==} + '@swc/core-linux-x64-musl@1.12.9': + resolution: {integrity: sha512-9FB0wM+6idCGTI20YsBNBg9xSWtkDBymnpaTCsZM3qDc0l4uOpJMqbfWhQvp17x7r/ulZfb2QY8RDvQmCL6AcQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.12.3': - resolution: {integrity: sha512-fxraM7exaPb1/W0CoHW45EFNOQUQh0nonBEcNFm2iv095mziBwttyxZyQBoDkQocpkd5NtsZw3xW5FTBPnn+Vw==} + '@swc/core-win32-arm64-msvc@1.12.9': + resolution: {integrity: sha512-zHOusMVbOH9ik5RtRrMiGzLpKwxrPXgXkBm3SbUCa65HAdjV33NZ0/R9Rv1uPESALtEl2tzMYLUxYA5ECFDFhA==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.12.3': - resolution: {integrity: sha512-FFIhMPXIDjRcewomwbYGPvem7Fj76AsuzbRahnAyp+OzJwrrtxVmra/kyUCfj4kix7vdGByY0WvVfiVCf5b7Mg==} + '@swc/core-win32-ia32-msvc@1.12.9': + resolution: {integrity: sha512-aWZf0PqE0ot7tCuhAjRkDFf41AzzSQO0x2xRfTbnhpROp57BRJ/N5eee1VULO/UA2PIJRG7GKQky5bSGBYlFug==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.12.3': - resolution: {integrity: sha512-Sf4iSg+IYT5AzFSDDmii08DfeKcvtkVxIuo+uS8BJMbiLjFNjgMkkVlBthknGyJcSK15ncg9248XjnM4jU8DZA==} + '@swc/core-win32-x64-msvc@1.12.9': + resolution: {integrity: sha512-C25fYftXOras3P3anSUeXXIpxmEkdAcsIL9yrr0j1xepTZ/yKwpnQ6g3coj8UXdeJy4GTVlR6+Ow/QiBgZQNOg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.12.3': - resolution: {integrity: sha512-c4NeXW8P3gPqcFwtm+4aH+F2Cj5KJLMiLaKhSj3mpv19glq+jmekomdktAw/VHyjsXlsmouOeNWrk8rVlkCRsg==} + '@swc/core@1.12.9': + resolution: {integrity: sha512-O+LfT2JlVMsIMWG9x+rdxg8GzpzeGtCZQfXV7cKc1PjIKUkLFf1QJ7okuseA4f/9vncu37dQ2ZcRrPKy0Ndd5g==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -960,6 +947,18 @@ packages: resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} engines: {node: '>=14.16'} + '@tc/mcp-nest@https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/025395f5f3a665e8439e481f84c25c6e6ed97933': + resolution: {tarball: https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/025395f5f3a665e8439e481f84c25c6e6ed97933} + version: 1.6.2 + peerDependencies: + '@modelcontextprotocol/sdk': '>=1.10.0' + '@nestjs/common': '>=9.0.0' + '@nestjs/core': '>=9.0.0' + express: '>=4.0.0' + reflect-metadata: '>=0.1.14' + zod: '>=3.0.0' + zod-to-json-schema: '>=3.23.0' + '@tokenizer/inflate@0.2.7': resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} engines: {node: '>=18'} @@ -1057,8 +1056,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@22.15.32': - resolution: {integrity: sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==} + '@types/node@22.16.0': + resolution: {integrity: sha512-B2egV9wALML1JCpv3VQoQ+yesQKAmNMBIAY7OteVrikcOcAkWm+dGL6qpeCktPjAv6N1JLnhbNiqS35UpFyBsQ==} '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -1090,63 +1089,63 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@typescript-eslint/eslint-plugin@8.34.1': - resolution: {integrity: sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==} + '@typescript-eslint/eslint-plugin@8.35.1': + resolution: {integrity: sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.34.1 + '@typescript-eslint/parser': ^8.35.1 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@8.34.1': - resolution: {integrity: sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==} + '@typescript-eslint/parser@8.35.1': + resolution: {integrity: sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/project-service@8.34.1': - resolution: {integrity: sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==} + '@typescript-eslint/project-service@8.35.1': + resolution: {integrity: sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@8.34.1': - resolution: {integrity: sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==} + '@typescript-eslint/scope-manager@8.35.1': + resolution: {integrity: sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.34.1': - resolution: {integrity: sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==} + '@typescript-eslint/tsconfig-utils@8.35.1': + resolution: {integrity: sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/type-utils@8.34.1': - resolution: {integrity: sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==} + '@typescript-eslint/type-utils@8.35.1': + resolution: {integrity: sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/types@8.34.1': - resolution: {integrity: sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==} + '@typescript-eslint/types@8.35.1': + resolution: {integrity: sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.34.1': - resolution: {integrity: sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==} + '@typescript-eslint/typescript-estree@8.35.1': + resolution: {integrity: sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.34.1': - resolution: {integrity: sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==} + '@typescript-eslint/utils@8.35.1': + resolution: {integrity: sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/visitor-keys@8.34.1': - resolution: {integrity: sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==} + '@typescript-eslint/visitor-keys@8.35.1': + resolution: {integrity: sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@webassemblyjs/ast@1.14.1': @@ -1415,8 +1414,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.25.0: - resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==} + browserslist@4.25.1: + resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1475,8 +1474,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001723: - resolution: {integrity: sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==} + caniuse-lite@1.0.30001726: + resolution: {integrity: sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1701,8 +1700,8 @@ packages: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} - dotenv@16.5.0: - resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} dunder-proto@1.0.1: @@ -1723,8 +1722,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.170: - resolution: {integrity: sha512-GP+M7aeluQo9uAyiTCxgIj/j+PrWhMlY7LFVj8prlsPljd0Fdg9AprlfUi+OCSFWy9Y5/2D/Jrj9HS8Z4rpKWA==} + electron-to-chromium@1.5.179: + resolution: {integrity: sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==} emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -1740,8 +1739,8 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - enhanced-resolve@5.18.1: - resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + enhanced-resolve@5.18.2: + resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==} engines: {node: '>=10.13.0'} error-ex@1.3.2: @@ -1787,8 +1786,8 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-plugin-prettier@5.5.0: - resolution: {integrity: sha512-8qsOYwkkGrahrgoUv76NZi23koqXOGiiEzXMrT8Q7VcYaUISR+5MorIUxfWqYXN0fN/31WbSrxCxFkVQ43wwrA==} + eslint-plugin-prettier@5.5.1: + resolution: {integrity: sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' @@ -1817,8 +1816,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.29.0: - resolution: {integrity: sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==} + eslint@9.30.1: + resolution: {integrity: sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1864,9 +1863,9 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - eventsource-parser@3.0.2: - resolution: {integrity: sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==} - engines: {node: '>=18.0.0'} + eventsource-parser@3.0.3: + resolution: {integrity: sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==} + engines: {node: '>=20.0.0'} eventsource@3.0.7: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} @@ -1884,11 +1883,11 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - express-rate-limit@7.5.0: - resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} + express-rate-limit@7.5.1: + resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} engines: {node: '>= 16'} peerDependencies: - express: ^4.11 || 5 || ^5.0.0-beta.1 + express: '>= 4.11' express@5.1.0: resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} @@ -2087,10 +2086,6 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -2880,8 +2875,8 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} - prettier@3.5.3: - resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true @@ -3263,8 +3258,8 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - token-types@6.0.0: - resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==} + token-types@6.0.3: + resolution: {integrity: sha512-IKJ6EzuPPWtKtEIEPpIdXv9j5j2LGJEYk0CKY2efgKoYKLBiZdh6iQkLVBow/CB3phyWAWCyk+bZeaimJn6uRQ==} engines: {node: '>=14.16'} tree-kill@1.2.2: @@ -3363,8 +3358,8 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript-eslint@8.34.1: - resolution: {integrity: sha512-XjS+b6Vg9oT1BaIUfkW3M3LvqZE++rbzAMEHuccCfO/YkP43ha6w3jTEMilQxMF92nVOYCcdjv1ZUhAa1D/0ow==} + typescript-eslint@8.35.1: + resolution: {integrity: sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -3438,8 +3433,8 @@ packages: resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} engines: {node: '>=6'} - webpack-sources@3.3.2: - resolution: {integrity: sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==} + webpack-sources@3.3.3: + resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} webpack@5.99.6: @@ -3518,20 +3513,20 @@ packages: resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} engines: {node: '>=18'} - zod-to-json-schema@3.24.5: - resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + zod-to-json-schema@3.24.6: + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} peerDependencies: zod: ^3.24.1 - zod@3.25.67: - resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} + zod@3.25.71: + resolution: {integrity: sha512-BsBc/NPk7h8WsUWYWYL+BajcJPY8YhjelaWu2NMLuzgraKAz4Lb4/6K11g9jpuDetjMiqhZ6YaexFLOC0Ogi3Q==} snapshots: '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 '@angular-devkit/core@19.2.6(chokidar@4.0.3)': dependencies: @@ -3555,11 +3550,11 @@ snapshots: optionalDependencies: chokidar: 4.0.3 - '@angular-devkit/schematics-cli@19.2.8(@types/node@22.15.32)(chokidar@4.0.3)': + '@angular-devkit/schematics-cli@19.2.8(@types/node@22.16.0)(chokidar@4.0.3)': dependencies: '@angular-devkit/core': 19.2.8(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.8(chokidar@4.0.3) - '@inquirer/prompts': 7.3.2(@types/node@22.15.32) + '@inquirer/prompts': 7.3.2(@types/node@22.16.0) ansi-colors: 4.1.3 symbol-observable: 4.0.0 yargs-parser: 21.1.1 @@ -3593,20 +3588,20 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.27.5': {} + '@babel/compat-data@7.28.0': {} - '@babel/core@7.27.4': + '@babel/core@7.28.0': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.5 + '@babel/generator': 7.28.0 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) '@babel/helpers': 7.27.6 - '@babel/parser': 7.27.5 + '@babel/parser': 7.28.0 '@babel/template': 7.27.2 - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.0 convert-source-map: 2.0.0 debug: 4.4.1 gensync: 1.0.0-beta.2 @@ -3615,35 +3610,37 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.27.5': + '@babel/generator@7.28.0': dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.0 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 jsesc: 3.1.0 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.27.5 + '@babel/compat-data': 7.28.0 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.25.0 + browserslist: 4.25.1 lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-globals@7.28.0': {} + '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.0 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': + '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.28.0 transitivePeerDependencies: - supports-color @@ -3658,116 +3655,116 @@ snapshots: '@babel/helpers@7.27.6': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.27.6 + '@babel/types': 7.28.0 - '@babel/parser@7.27.5': + '@babel/parser@7.28.0': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.28.0 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.27.4)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.27.4)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.27.4)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.27.4)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.27.4)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.27.4)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.27.4)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.27.4)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.27.4)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.27.4)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.27.4)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.27.4)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.27.4)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.27.4)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.0 - '@babel/traverse@7.27.4': + '@babel/traverse@7.28.0': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.5 - '@babel/parser': 7.27.5 + '@babel/generator': 7.28.0 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.0 '@babel/template': 7.27.2 - '@babel/types': 7.27.6 + '@babel/types': 7.28.0 debug: 4.4.1 - globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.27.6': + '@babel/types@7.28.0': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 @@ -3781,14 +3778,14 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0)': + '@eslint-community/eslint-utils@4.7.0(eslint@9.30.1)': dependencies: - eslint: 9.29.0 + eslint: 9.30.1 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.20.1': + '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 debug: 4.4.1 @@ -3796,13 +3793,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.2.3': {} + '@eslint/config-helpers@0.3.0': {} '@eslint/core@0.14.0': dependencies: '@types/json-schema': 7.0.15 - '@eslint/core@0.15.0': + '@eslint/core@0.15.1': dependencies: '@types/json-schema': 7.0.15 @@ -3820,13 +3817,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.29.0': {} + '@eslint/js@9.30.1': {} '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.3.2': + '@eslint/plugin-kit@0.3.3': dependencies: - '@eslint/core': 0.15.0 + '@eslint/core': 0.15.1 levn: 0.4.1 '@humanfs/core@0.19.1': {} @@ -3842,27 +3839,27 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/checkbox@4.1.8(@types/node@22.15.32)': + '@inquirer/checkbox@4.1.9(@types/node@22.16.0)': dependencies: - '@inquirer/core': 10.1.13(@types/node@22.15.32) + '@inquirer/core': 10.1.14(@types/node@22.16.0) '@inquirer/figures': 1.0.12 - '@inquirer/type': 3.0.7(@types/node@22.15.32) + '@inquirer/type': 3.0.7(@types/node@22.16.0) ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 - '@inquirer/confirm@5.1.12(@types/node@22.15.32)': + '@inquirer/confirm@5.1.13(@types/node@22.16.0)': dependencies: - '@inquirer/core': 10.1.13(@types/node@22.15.32) - '@inquirer/type': 3.0.7(@types/node@22.15.32) + '@inquirer/core': 10.1.14(@types/node@22.16.0) + '@inquirer/type': 3.0.7(@types/node@22.16.0) optionalDependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 - '@inquirer/core@10.1.13(@types/node@22.15.32)': + '@inquirer/core@10.1.14(@types/node@22.16.0)': dependencies: '@inquirer/figures': 1.0.12 - '@inquirer/type': 3.0.7(@types/node@22.15.32) + '@inquirer/type': 3.0.7(@types/node@22.16.0) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 @@ -3870,108 +3867,108 @@ snapshots: wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 - '@inquirer/editor@4.2.13(@types/node@22.15.32)': + '@inquirer/editor@4.2.14(@types/node@22.16.0)': dependencies: - '@inquirer/core': 10.1.13(@types/node@22.15.32) - '@inquirer/type': 3.0.7(@types/node@22.15.32) + '@inquirer/core': 10.1.14(@types/node@22.16.0) + '@inquirer/type': 3.0.7(@types/node@22.16.0) external-editor: 3.1.0 optionalDependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 - '@inquirer/expand@4.0.15(@types/node@22.15.32)': + '@inquirer/expand@4.0.16(@types/node@22.16.0)': dependencies: - '@inquirer/core': 10.1.13(@types/node@22.15.32) - '@inquirer/type': 3.0.7(@types/node@22.15.32) + '@inquirer/core': 10.1.14(@types/node@22.16.0) + '@inquirer/type': 3.0.7(@types/node@22.16.0) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 '@inquirer/figures@1.0.12': {} - '@inquirer/input@4.1.12(@types/node@22.15.32)': + '@inquirer/input@4.2.0(@types/node@22.16.0)': dependencies: - '@inquirer/core': 10.1.13(@types/node@22.15.32) - '@inquirer/type': 3.0.7(@types/node@22.15.32) + '@inquirer/core': 10.1.14(@types/node@22.16.0) + '@inquirer/type': 3.0.7(@types/node@22.16.0) optionalDependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 - '@inquirer/number@3.0.15(@types/node@22.15.32)': + '@inquirer/number@3.0.16(@types/node@22.16.0)': dependencies: - '@inquirer/core': 10.1.13(@types/node@22.15.32) - '@inquirer/type': 3.0.7(@types/node@22.15.32) + '@inquirer/core': 10.1.14(@types/node@22.16.0) + '@inquirer/type': 3.0.7(@types/node@22.16.0) optionalDependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 - '@inquirer/password@4.0.15(@types/node@22.15.32)': + '@inquirer/password@4.0.16(@types/node@22.16.0)': dependencies: - '@inquirer/core': 10.1.13(@types/node@22.15.32) - '@inquirer/type': 3.0.7(@types/node@22.15.32) + '@inquirer/core': 10.1.14(@types/node@22.16.0) + '@inquirer/type': 3.0.7(@types/node@22.16.0) ansi-escapes: 4.3.2 optionalDependencies: - '@types/node': 22.15.32 - - '@inquirer/prompts@7.3.2(@types/node@22.15.32)': - dependencies: - '@inquirer/checkbox': 4.1.8(@types/node@22.15.32) - '@inquirer/confirm': 5.1.12(@types/node@22.15.32) - '@inquirer/editor': 4.2.13(@types/node@22.15.32) - '@inquirer/expand': 4.0.15(@types/node@22.15.32) - '@inquirer/input': 4.1.12(@types/node@22.15.32) - '@inquirer/number': 3.0.15(@types/node@22.15.32) - '@inquirer/password': 4.0.15(@types/node@22.15.32) - '@inquirer/rawlist': 4.1.3(@types/node@22.15.32) - '@inquirer/search': 3.0.15(@types/node@22.15.32) - '@inquirer/select': 4.2.3(@types/node@22.15.32) + '@types/node': 22.16.0 + + '@inquirer/prompts@7.3.2(@types/node@22.16.0)': + dependencies: + '@inquirer/checkbox': 4.1.9(@types/node@22.16.0) + '@inquirer/confirm': 5.1.13(@types/node@22.16.0) + '@inquirer/editor': 4.2.14(@types/node@22.16.0) + '@inquirer/expand': 4.0.16(@types/node@22.16.0) + '@inquirer/input': 4.2.0(@types/node@22.16.0) + '@inquirer/number': 3.0.16(@types/node@22.16.0) + '@inquirer/password': 4.0.16(@types/node@22.16.0) + '@inquirer/rawlist': 4.1.4(@types/node@22.16.0) + '@inquirer/search': 3.0.16(@types/node@22.16.0) + '@inquirer/select': 4.2.4(@types/node@22.16.0) optionalDependencies: - '@types/node': 22.15.32 - - '@inquirer/prompts@7.4.1(@types/node@22.15.32)': - dependencies: - '@inquirer/checkbox': 4.1.8(@types/node@22.15.32) - '@inquirer/confirm': 5.1.12(@types/node@22.15.32) - '@inquirer/editor': 4.2.13(@types/node@22.15.32) - '@inquirer/expand': 4.0.15(@types/node@22.15.32) - '@inquirer/input': 4.1.12(@types/node@22.15.32) - '@inquirer/number': 3.0.15(@types/node@22.15.32) - '@inquirer/password': 4.0.15(@types/node@22.15.32) - '@inquirer/rawlist': 4.1.3(@types/node@22.15.32) - '@inquirer/search': 3.0.15(@types/node@22.15.32) - '@inquirer/select': 4.2.3(@types/node@22.15.32) + '@types/node': 22.16.0 + + '@inquirer/prompts@7.4.1(@types/node@22.16.0)': + dependencies: + '@inquirer/checkbox': 4.1.9(@types/node@22.16.0) + '@inquirer/confirm': 5.1.13(@types/node@22.16.0) + '@inquirer/editor': 4.2.14(@types/node@22.16.0) + '@inquirer/expand': 4.0.16(@types/node@22.16.0) + '@inquirer/input': 4.2.0(@types/node@22.16.0) + '@inquirer/number': 3.0.16(@types/node@22.16.0) + '@inquirer/password': 4.0.16(@types/node@22.16.0) + '@inquirer/rawlist': 4.1.4(@types/node@22.16.0) + '@inquirer/search': 3.0.16(@types/node@22.16.0) + '@inquirer/select': 4.2.4(@types/node@22.16.0) optionalDependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 - '@inquirer/rawlist@4.1.3(@types/node@22.15.32)': + '@inquirer/rawlist@4.1.4(@types/node@22.16.0)': dependencies: - '@inquirer/core': 10.1.13(@types/node@22.15.32) - '@inquirer/type': 3.0.7(@types/node@22.15.32) + '@inquirer/core': 10.1.14(@types/node@22.16.0) + '@inquirer/type': 3.0.7(@types/node@22.16.0) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 - '@inquirer/search@3.0.15(@types/node@22.15.32)': + '@inquirer/search@3.0.16(@types/node@22.16.0)': dependencies: - '@inquirer/core': 10.1.13(@types/node@22.15.32) + '@inquirer/core': 10.1.14(@types/node@22.16.0) '@inquirer/figures': 1.0.12 - '@inquirer/type': 3.0.7(@types/node@22.15.32) + '@inquirer/type': 3.0.7(@types/node@22.16.0) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 - '@inquirer/select@4.2.3(@types/node@22.15.32)': + '@inquirer/select@4.2.4(@types/node@22.16.0)': dependencies: - '@inquirer/core': 10.1.13(@types/node@22.15.32) + '@inquirer/core': 10.1.14(@types/node@22.16.0) '@inquirer/figures': 1.0.12 - '@inquirer/type': 3.0.7(@types/node@22.15.32) + '@inquirer/type': 3.0.7(@types/node@22.16.0) ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 - '@inquirer/type@3.0.7(@types/node@22.15.32)': + '@inquirer/type@3.0.7(@types/node@22.16.0)': optionalDependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 '@isaacs/balanced-match@4.0.1': {} @@ -4001,27 +3998,27 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.32 + '@types/node': 22.16.0 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3))': + '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.32 + '@types/node': 22.16.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)) + jest-config: 29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -4046,7 +4043,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.32 + '@types/node': 22.16.0 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -4064,7 +4061,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.15.32 + '@types/node': 22.16.0 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -4085,8 +4082,8 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.15.32 + '@jridgewell/trace-mapping': 0.3.29 + '@types/node': 22.16.0 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -4113,7 +4110,7 @@ snapshots: '@jest/source-map@29.6.3': dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 callsites: 3.1.0 graceful-fs: 4.2.11 @@ -4133,9 +4130,9 @@ snapshots: '@jest/transform@29.7.0': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 convert-source-map: 2.0.0 @@ -4156,52 +4153,50 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.15.32 + '@types/node': 22.16.0 '@types/yargs': 17.0.33 chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.8': + '@jridgewell/gen-mapping@0.3.12': dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/trace-mapping': 0.3.29 '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/set-array@1.2.1': {} - - '@jridgewell/source-map@0.3.6': + '@jridgewell/source-map@0.3.10': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 - '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.4': {} - '@jridgewell/trace-mapping@0.3.25': + '@jridgewell/trace-mapping@0.3.29': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.4 '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.4 '@lukeed/csprng@1.1.0': {} - '@modelcontextprotocol/sdk@1.13.0': + '@modelcontextprotocol/sdk@1.13.3': dependencies: ajv: 6.12.6 content-type: 1.0.5 cors: 2.8.5 cross-spawn: 7.0.6 eventsource: 3.0.7 + eventsource-parser: 3.0.3 express: 5.1.0 - express-rate-limit: 7.5.0(express@5.1.0) + express-rate-limit: 7.5.1(express@5.1.0) pkce-challenge: 5.0.0 raw-body: 3.0.0 - zod: 3.25.67 - zod-to-json-schema: 3.24.5(zod@3.25.67) + zod: 3.25.71 + zod-to-json-schema: 3.24.6(zod@3.25.71) transitivePeerDependencies: - supports-color @@ -4273,18 +4268,18 @@ snapshots: '@napi-rs/nice-win32-x64-msvc': 1.0.1 optional: true - '@nestjs/cli@11.0.7(@swc/cli@0.6.0(@swc/core@1.12.3)(chokidar@4.0.3))(@swc/core@1.12.3)(@types/node@22.15.32)': + '@nestjs/cli@11.0.7(@swc/cli@0.6.0(@swc/core@1.12.9)(chokidar@4.0.3))(@swc/core@1.12.9)(@types/node@22.16.0)': dependencies: '@angular-devkit/core': 19.2.8(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.8(chokidar@4.0.3) - '@angular-devkit/schematics-cli': 19.2.8(@types/node@22.15.32)(chokidar@4.0.3) - '@inquirer/prompts': 7.4.1(@types/node@22.15.32) + '@angular-devkit/schematics-cli': 19.2.8(@types/node@22.16.0)(chokidar@4.0.3) + '@inquirer/prompts': 7.4.1(@types/node@22.16.0) '@nestjs/schematics': 11.0.5(chokidar@4.0.3)(typescript@5.8.3) ansis: 3.17.0 chokidar: 4.0.3 cli-table3: 0.6.5 commander: 4.1.1 - fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.8.3)(webpack@5.99.6(@swc/core@1.12.3)) + fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.8.3)(webpack@5.99.6(@swc/core@1.12.9)) glob: 11.0.1 node-emoji: 1.11.0 ora: 5.4.1 @@ -4292,11 +4287,11 @@ snapshots: tsconfig-paths: 4.2.0 tsconfig-paths-webpack-plugin: 4.2.0 typescript: 5.8.3 - webpack: 5.99.6(@swc/core@1.12.3) + webpack: 5.99.6(@swc/core@1.12.9) webpack-node-externals: 3.0.0 optionalDependencies: - '@swc/cli': 0.6.0(@swc/core@1.12.3)(chokidar@4.0.3) - '@swc/core': 1.12.3 + '@swc/cli': 0.6.0(@swc/core@1.12.9)(chokidar@4.0.3) + '@swc/core': 1.12.9 transitivePeerDependencies: - '@types/node' - esbuild @@ -4387,17 +4382,6 @@ snapshots: '@pkgr/core@0.2.7': {} - '@rekog/mcp-nest@https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/8950083c999f7636fed9f62b26330748aed9ed30(@modelcontextprotocol/sdk@1.13.0)(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(express@5.1.0)(reflect-metadata@0.2.2)(zod-to-json-schema@3.24.5(zod@3.25.67))(zod@3.25.67)': - dependencies: - '@modelcontextprotocol/sdk': 1.13.0 - '@nestjs/common': 11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.3(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - express: 5.1.0 - path-to-regexp: 8.2.0 - reflect-metadata: 0.2.2 - zod: 3.25.67 - zod-to-json-schema: 3.24.5(zod@3.25.67) - '@sec-ant/readable-stream@0.4.1': {} '@sinclair/typebox@0.27.8': {} @@ -4412,9 +4396,9 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@swc/cli@0.6.0(@swc/core@1.12.3)(chokidar@4.0.3)': + '@swc/cli@0.6.0(@swc/core@1.12.9)(chokidar@4.0.3)': dependencies: - '@swc/core': 1.12.3 + '@swc/core': 1.12.9 '@swc/counter': 0.1.3 '@xhmikosr/bin-wrapper': 13.0.5 commander: 8.3.0 @@ -4427,51 +4411,51 @@ snapshots: optionalDependencies: chokidar: 4.0.3 - '@swc/core-darwin-arm64@1.12.3': + '@swc/core-darwin-arm64@1.12.9': optional: true - '@swc/core-darwin-x64@1.12.3': + '@swc/core-darwin-x64@1.12.9': optional: true - '@swc/core-linux-arm-gnueabihf@1.12.3': + '@swc/core-linux-arm-gnueabihf@1.12.9': optional: true - '@swc/core-linux-arm64-gnu@1.12.3': + '@swc/core-linux-arm64-gnu@1.12.9': optional: true - '@swc/core-linux-arm64-musl@1.12.3': + '@swc/core-linux-arm64-musl@1.12.9': optional: true - '@swc/core-linux-x64-gnu@1.12.3': + '@swc/core-linux-x64-gnu@1.12.9': optional: true - '@swc/core-linux-x64-musl@1.12.3': + '@swc/core-linux-x64-musl@1.12.9': optional: true - '@swc/core-win32-arm64-msvc@1.12.3': + '@swc/core-win32-arm64-msvc@1.12.9': optional: true - '@swc/core-win32-ia32-msvc@1.12.3': + '@swc/core-win32-ia32-msvc@1.12.9': optional: true - '@swc/core-win32-x64-msvc@1.12.3': + '@swc/core-win32-x64-msvc@1.12.9': optional: true - '@swc/core@1.12.3': + '@swc/core@1.12.9': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.23 optionalDependencies: - '@swc/core-darwin-arm64': 1.12.3 - '@swc/core-darwin-x64': 1.12.3 - '@swc/core-linux-arm-gnueabihf': 1.12.3 - '@swc/core-linux-arm64-gnu': 1.12.3 - '@swc/core-linux-arm64-musl': 1.12.3 - '@swc/core-linux-x64-gnu': 1.12.3 - '@swc/core-linux-x64-musl': 1.12.3 - '@swc/core-win32-arm64-msvc': 1.12.3 - '@swc/core-win32-ia32-msvc': 1.12.3 - '@swc/core-win32-x64-msvc': 1.12.3 + '@swc/core-darwin-arm64': 1.12.9 + '@swc/core-darwin-x64': 1.12.9 + '@swc/core-linux-arm-gnueabihf': 1.12.9 + '@swc/core-linux-arm64-gnu': 1.12.9 + '@swc/core-linux-arm64-musl': 1.12.9 + '@swc/core-linux-x64-gnu': 1.12.9 + '@swc/core-linux-x64-musl': 1.12.9 + '@swc/core-win32-arm64-msvc': 1.12.9 + '@swc/core-win32-ia32-msvc': 1.12.9 + '@swc/core-win32-x64-msvc': 1.12.9 '@swc/counter@0.1.3': {} @@ -4483,11 +4467,22 @@ snapshots: dependencies: defer-to-connect: 2.0.1 + '@tc/mcp-nest@https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/025395f5f3a665e8439e481f84c25c6e6ed97933(@modelcontextprotocol/sdk@1.13.3)(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(express@5.1.0)(reflect-metadata@0.2.2)(zod-to-json-schema@3.24.6(zod@3.25.71))(zod@3.25.71)': + dependencies: + '@modelcontextprotocol/sdk': 1.13.3 + '@nestjs/common': 11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.3(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) + express: 5.1.0 + path-to-regexp: 8.2.0 + reflect-metadata: 0.2.2 + zod: 3.25.71 + zod-to-json-schema: 3.24.6(zod@3.25.71) + '@tokenizer/inflate@0.2.7': dependencies: debug: 4.4.1 fflate: 0.8.2 - token-types: 6.0.0 + token-types: 6.0.3 transitivePeerDependencies: - supports-color @@ -4503,33 +4498,33 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.0 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.7 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.28.0 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.0 '@types/babel__traverse@7.20.7': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.28.0 '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.15.32 + '@types/node': 22.16.0 '@types/connect@3.4.38': dependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 '@types/cookiejar@2.1.5': {} @@ -4547,14 +4542,14 @@ snapshots: '@types/express-serve-static-core@4.19.6': dependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 '@types/express-serve-static-core@5.0.6': dependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 @@ -4574,7 +4569,7 @@ snapshots: '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 '@types/http-cache-semantics@4.0.4': {} @@ -4600,7 +4595,7 @@ snapshots: '@types/jsonwebtoken@9.0.10': dependencies: '@types/ms': 2.1.0 - '@types/node': 22.15.32 + '@types/node': 22.16.0 '@types/methods@1.1.4': {} @@ -4608,7 +4603,7 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@22.15.32': + '@types/node@22.16.0': dependencies: undici-types: 6.21.0 @@ -4619,12 +4614,12 @@ snapshots: '@types/send@0.17.5': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.15.32 + '@types/node': 22.16.0 '@types/serve-static@1.15.8': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 22.15.32 + '@types/node': 22.16.0 '@types/send': 0.17.5 '@types/stack-utils@2.0.3': {} @@ -4633,7 +4628,7 @@ snapshots: dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 22.15.32 + '@types/node': 22.16.0 form-data: 4.0.3 '@types/supertest@6.0.3': @@ -4649,15 +4644,15 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.34.1(eslint@9.29.0)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.34.1 - '@typescript-eslint/type-utils': 8.34.1(eslint@9.29.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.34.1(eslint@9.29.0)(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.34.1 - eslint: 9.29.0 + '@typescript-eslint/parser': 8.35.1(eslint@9.30.1)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.35.1 + '@typescript-eslint/type-utils': 8.35.1(eslint@9.30.1)(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.1(eslint@9.30.1)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.1 + eslint: 9.30.1 graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 @@ -4666,55 +4661,55 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.34.1(eslint@9.29.0)(typescript@5.8.3)': + '@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 8.34.1 - '@typescript-eslint/types': 8.34.1 - '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.34.1 + '@typescript-eslint/scope-manager': 8.35.1 + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.1 debug: 4.4.1 - eslint: 9.29.0 + eslint: 9.30.1 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.34.1(typescript@5.8.3)': + '@typescript-eslint/project-service@8.35.1(typescript@5.8.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.34.1(typescript@5.8.3) - '@typescript-eslint/types': 8.34.1 + '@typescript-eslint/tsconfig-utils': 8.35.1(typescript@5.8.3) + '@typescript-eslint/types': 8.35.1 debug: 4.4.1 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.34.1': + '@typescript-eslint/scope-manager@8.35.1': dependencies: - '@typescript-eslint/types': 8.34.1 - '@typescript-eslint/visitor-keys': 8.34.1 + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/visitor-keys': 8.35.1 - '@typescript-eslint/tsconfig-utils@8.34.1(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.35.1(typescript@5.8.3)': dependencies: typescript: 5.8.3 - '@typescript-eslint/type-utils@8.34.1(eslint@9.29.0)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.35.1(eslint@9.30.1)(typescript@5.8.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.34.1(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.1(eslint@9.30.1)(typescript@5.8.3) debug: 4.4.1 - eslint: 9.29.0 + eslint: 9.30.1 ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.34.1': {} + '@typescript-eslint/types@8.35.1': {} - '@typescript-eslint/typescript-estree@8.34.1(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.35.1(typescript@5.8.3)': dependencies: - '@typescript-eslint/project-service': 8.34.1(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.34.1(typescript@5.8.3) - '@typescript-eslint/types': 8.34.1 - '@typescript-eslint/visitor-keys': 8.34.1 + '@typescript-eslint/project-service': 8.35.1(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.35.1(typescript@5.8.3) + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/visitor-keys': 8.35.1 debug: 4.4.1 fast-glob: 3.3.3 is-glob: 4.0.3 @@ -4725,20 +4720,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.34.1(eslint@9.29.0)(typescript@5.8.3)': + '@typescript-eslint/utils@8.35.1(eslint@9.30.1)(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0) - '@typescript-eslint/scope-manager': 8.34.1 - '@typescript-eslint/types': 8.34.1 - '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) - eslint: 9.29.0 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1) + '@typescript-eslint/scope-manager': 8.35.1 + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3) + eslint: 9.30.1 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.34.1': + '@typescript-eslint/visitor-keys@8.35.1': dependencies: - '@typescript-eslint/types': 8.34.1 + '@typescript-eslint/types': 8.35.1 eslint-visitor-keys: 4.2.1 '@webassemblyjs/ast@1.14.1': @@ -4982,13 +4977,13 @@ snapshots: b4a@1.6.7: {} - babel-jest@29.7.0(@babel/core@7.27.4): + babel-jest@29.7.0(@babel/core@7.28.0): dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@jest/transform': 29.7.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.27.4) + babel-preset-jest: 29.6.3(@babel/core@7.28.0) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -5008,34 +5003,34 @@ snapshots: babel-plugin-jest-hoist@29.6.3: dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.27.6 + '@babel/types': 7.28.0 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.7 - babel-preset-current-node-syntax@1.1.0(@babel/core@7.27.4): - dependencies: - '@babel/core': 7.27.4 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.27.4) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.27.4) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.27.4) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.27.4) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.27.4) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.27.4) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.27.4) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.27.4) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.27.4) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.27.4) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.27.4) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.27.4) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.27.4) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.27.4) - - babel-preset-jest@29.6.3(@babel/core@7.27.4): - dependencies: - '@babel/core': 7.27.4 + babel-preset-current-node-syntax@1.1.0(@babel/core@7.28.0): + dependencies: + '@babel/core': 7.28.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.0) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.0) + + babel-preset-jest@29.6.3(@babel/core@7.28.0): + dependencies: + '@babel/core': 7.28.0 babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.27.4) + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.28.0) balanced-match@1.0.2: {} @@ -5088,12 +5083,12 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.25.0: + browserslist@4.25.1: dependencies: - caniuse-lite: 1.0.30001723 - electron-to-chromium: 1.5.170 + caniuse-lite: 1.0.30001726 + electron-to-chromium: 1.5.179 node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.0) + update-browserslist-db: 1.1.3(browserslist@4.25.1) bs-logger@0.2.6: dependencies: @@ -5148,7 +5143,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001723: {} + caniuse-lite@1.0.30001726: {} chalk@4.1.2: dependencies: @@ -5276,13 +5271,13 @@ snapshots: optionalDependencies: typescript: 5.8.3 - create-jest@29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)): + create-jest@29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)) + jest-config: 29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -5336,7 +5331,7 @@ snapshots: diff@4.0.2: {} - dotenv@16.5.0: {} + dotenv@16.6.1: {} dunder-proto@1.0.1: dependencies: @@ -5356,7 +5351,7 @@ snapshots: dependencies: jake: 10.9.2 - electron-to-chromium@1.5.170: {} + electron-to-chromium@1.5.179: {} emittery@0.13.1: {} @@ -5366,7 +5361,7 @@ snapshots: encodeurl@2.0.0: {} - enhanced-resolve@5.18.1: + enhanced-resolve@5.18.2: dependencies: graceful-fs: 4.2.11 tapable: 2.2.2 @@ -5400,19 +5395,19 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.5(eslint@9.29.0): + eslint-config-prettier@10.1.5(eslint@9.30.1): dependencies: - eslint: 9.29.0 + eslint: 9.30.1 - eslint-plugin-prettier@5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@10.1.5(eslint@9.29.0))(eslint@9.29.0)(prettier@3.5.3): + eslint-plugin-prettier@5.5.1(@types/eslint@9.6.1)(eslint-config-prettier@10.1.5(eslint@9.30.1))(eslint@9.30.1)(prettier@3.6.2): dependencies: - eslint: 9.29.0 - prettier: 3.5.3 + eslint: 9.30.1 + prettier: 3.6.2 prettier-linter-helpers: 1.0.0 synckit: 0.11.8 optionalDependencies: '@types/eslint': 9.6.1 - eslint-config-prettier: 10.1.5(eslint@9.29.0) + eslint-config-prettier: 10.1.5(eslint@9.30.1) eslint-scope@5.1.1: dependencies: @@ -5428,16 +5423,16 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.29.0: + eslint@9.30.1: dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.20.1 - '@eslint/config-helpers': 0.2.3 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.0 '@eslint/core': 0.14.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.29.0 - '@eslint/plugin-kit': 0.3.2 + '@eslint/js': 9.30.1 + '@eslint/plugin-kit': 0.3.3 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 @@ -5494,11 +5489,11 @@ snapshots: events@3.3.0: {} - eventsource-parser@3.0.2: {} + eventsource-parser@3.0.3: {} eventsource@3.0.7: dependencies: - eventsource-parser: 3.0.2 + eventsource-parser: 3.0.3 execa@5.1.1: dependencies: @@ -5522,7 +5517,7 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - express-rate-limit@7.5.0(express@5.1.0): + express-rate-limit@7.5.1(express@5.1.0): dependencies: express: 5.1.0 @@ -5613,14 +5608,14 @@ snapshots: dependencies: get-stream: 9.0.1 strtok3: 9.1.1 - token-types: 6.0.0 + token-types: 6.0.3 uint8array-extras: 1.4.0 file-type@21.0.0: dependencies: '@tokenizer/inflate': 0.2.7 strtok3: 10.3.1 - token-types: 6.0.0 + token-types: 6.0.3 uint8array-extras: 1.4.0 transitivePeerDependencies: - supports-color @@ -5676,7 +5671,7 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - fork-ts-checker-webpack-plugin@9.1.0(typescript@5.8.3)(webpack@5.99.6(@swc/core@1.12.3)): + fork-ts-checker-webpack-plugin@9.1.0(typescript@5.8.3)(webpack@5.99.6(@swc/core@1.12.9)): dependencies: '@babel/code-frame': 7.27.1 chalk: 4.1.2 @@ -5691,7 +5686,7 @@ snapshots: semver: 7.7.2 tapable: 2.2.2 typescript: 5.8.3 - webpack: 5.99.6(@swc/core@1.12.3) + webpack: 5.99.6(@swc/core@1.12.9) form-data-encoder@2.1.4: {} @@ -5787,8 +5782,6 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 - globals@11.12.0: {} - globals@14.0.0: {} globals@15.15.0: {} @@ -5921,8 +5914,8 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: - '@babel/core': 7.27.4 - '@babel/parser': 7.27.5 + '@babel/core': 7.28.0 + '@babel/parser': 7.28.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -5931,8 +5924,8 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.27.4 - '@babel/parser': 7.27.5 + '@babel/core': 7.28.0 + '@babel/parser': 7.28.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.7.2 @@ -5983,7 +5976,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.32 + '@types/node': 22.16.0 chalk: 4.1.2 co: 4.6.0 dedent: 1.6.0 @@ -6003,16 +5996,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)): + jest-cli@29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)) + create-jest: 29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)) + jest-config: 29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -6022,12 +6015,12 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)): + jest-config@29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)): dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.27.4) + babel-jest: 29.7.0(@babel/core@7.28.0) chalk: 4.1.2 ci-info: 3.9.0 deepmerge: 4.3.1 @@ -6047,8 +6040,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 22.15.32 - ts-node: 10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3) + '@types/node': 22.16.0 + ts-node: 10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -6077,7 +6070,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.32 + '@types/node': 22.16.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -6087,7 +6080,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.15.32 + '@types/node': 22.16.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -6126,7 +6119,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.32 + '@types/node': 22.16.0 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -6161,7 +6154,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.32 + '@types/node': 22.16.0 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -6189,7 +6182,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.32 + '@types/node': 22.16.0 chalk: 4.1.2 cjs-module-lexer: 1.4.3 collect-v8-coverage: 1.0.2 @@ -6209,15 +6202,15 @@ snapshots: jest-snapshot@29.7.0: dependencies: - '@babel/core': 7.27.4 - '@babel/generator': 7.27.5 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.27.4) - '@babel/types': 7.27.6 + '@babel/core': 7.28.0 + '@babel/generator': 7.28.0 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.0) + '@babel/types': 7.28.0 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.27.4) + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.28.0) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -6235,7 +6228,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.32 + '@types/node': 22.16.0 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -6254,7 +6247,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.32 + '@types/node': 22.16.0 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -6263,23 +6256,23 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 22.15.32 + '@types/node': 22.16.0 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)): + jest@29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)) + jest-cli: 29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -6437,7 +6430,7 @@ snapshots: magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.4 make-dir@4.0.0: dependencies: @@ -6677,7 +6670,7 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier@3.5.3: {} + prettier@3.6.2: {} pretty-format@29.7.0: dependencies: @@ -7030,20 +7023,20 @@ snapshots: fast-fifo: 1.3.2 streamx: 2.22.1 - terser-webpack-plugin@5.3.14(@swc/core@1.12.3)(webpack@5.99.6(@swc/core@1.12.3)): + terser-webpack-plugin@5.3.14(@swc/core@1.12.9)(webpack@5.99.6(@swc/core@1.12.9)): dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 jest-worker: 27.5.1 schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.99.6(@swc/core@1.12.3) + webpack: 5.99.6(@swc/core@1.12.9) optionalDependencies: - '@swc/core': 1.12.3 + '@swc/core': 1.12.9 terser@5.43.1: dependencies: - '@jridgewell/source-map': 0.3.6 + '@jridgewell/source-map': 0.3.10 acorn: 8.15.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -7072,7 +7065,7 @@ snapshots: toidentifier@1.0.1: {} - token-types@6.0.0: + token-types@6.0.3: dependencies: '@tokenizer/token': 0.3.0 ieee754: 1.2.1 @@ -7083,12 +7076,12 @@ snapshots: dependencies: typescript: 5.8.3 - ts-jest@29.4.0(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)))(typescript@5.8.3): + ts-jest@29.4.0(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.15.32)(ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3)) + jest: 29.7.0(@types/node@22.16.0)(ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 @@ -7097,30 +7090,30 @@ snapshots: typescript: 5.8.3 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.28.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.27.4) + babel-jest: 29.7.0(@babel/core@7.28.0) jest-util: 29.7.0 - ts-loader@9.5.2(typescript@5.8.3)(webpack@5.99.6(@swc/core@1.12.3)): + ts-loader@9.5.2(typescript@5.8.3)(webpack@5.99.6(@swc/core@1.12.9)): dependencies: chalk: 4.1.2 - enhanced-resolve: 5.18.1 + enhanced-resolve: 5.18.2 micromatch: 4.0.8 semver: 7.7.2 source-map: 0.7.4 typescript: 5.8.3 - webpack: 5.99.6(@swc/core@1.12.3) + webpack: 5.99.6(@swc/core@1.12.9) - ts-node@10.9.2(@swc/core@1.12.3)(@types/node@22.15.32)(typescript@5.8.3): + ts-node@10.9.2(@swc/core@1.12.9)(@types/node@22.16.0)(typescript@5.8.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.15.32 + '@types/node': 22.16.0 acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -7131,12 +7124,12 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.12.3 + '@swc/core': 1.12.9 tsconfig-paths-webpack-plugin@4.2.0: dependencies: chalk: 4.1.2 - enhanced-resolve: 5.18.1 + enhanced-resolve: 5.18.2 tapable: 2.2.2 tsconfig-paths: 4.2.0 @@ -7171,12 +7164,12 @@ snapshots: typedarray@0.0.6: {} - typescript-eslint@8.34.1(eslint@9.29.0)(typescript@5.8.3): + typescript-eslint@8.35.1(eslint@9.30.1)(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3) - '@typescript-eslint/parser': 8.34.1(eslint@9.29.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.34.1(eslint@9.29.0)(typescript@5.8.3) - eslint: 9.29.0 + '@typescript-eslint/eslint-plugin': 8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1)(typescript@5.8.3) + '@typescript-eslint/parser': 8.35.1(eslint@9.30.1)(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.1(eslint@9.30.1)(typescript@5.8.3) + eslint: 9.30.1 typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -7200,9 +7193,9 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.1.3(browserslist@4.25.0): + update-browserslist-db@1.1.3(browserslist@4.25.1): dependencies: - browserslist: 4.25.0 + browserslist: 4.25.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -7216,7 +7209,7 @@ snapshots: v8-to-istanbul@9.3.0: dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 @@ -7239,9 +7232,9 @@ snapshots: webpack-node-externals@3.0.0: {} - webpack-sources@3.3.2: {} + webpack-sources@3.3.3: {} - webpack@5.99.6(@swc/core@1.12.3): + webpack@5.99.6(@swc/core@1.12.9): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -7249,9 +7242,9 @@ snapshots: '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 - browserslist: 4.25.0 + browserslist: 4.25.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.1 + enhanced-resolve: 5.18.2 es-module-lexer: 1.7.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -7263,9 +7256,9 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.2 tapable: 2.2.2 - terser-webpack-plugin: 5.3.14(@swc/core@1.12.3)(webpack@5.99.6(@swc/core@1.12.3)) + terser-webpack-plugin: 5.3.14(@swc/core@1.12.9)(webpack@5.99.6(@swc/core@1.12.9)) watchpack: 2.4.4 - webpack-sources: 3.3.2 + webpack-sources: 3.3.3 transitivePeerDependencies: - '@swc/core' - esbuild @@ -7333,8 +7326,8 @@ snapshots: yoctocolors-cjs@2.1.2: {} - zod-to-json-schema@3.24.5(zod@3.25.67): + zod-to-json-schema@3.24.6(zod@3.25.71): dependencies: - zod: 3.25.67 + zod: 3.25.71 - zod@3.25.67: {} + zod@3.25.71: {} From 80f51aee06b9e5e94180af8592398fabe03059b8 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 10 Jul 2025 11:55:38 +0300 Subject: [PATCH 08/32] adds tc-agent --- .github/workflows/tc_agent.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/tc_agent.yml diff --git a/.github/workflows/tc_agent.yml b/.github/workflows/tc_agent.yml new file mode 100644 index 0000000..47cc16f --- /dev/null +++ b/.github/workflows/tc_agent.yml @@ -0,0 +1,22 @@ +name: TC Action Agent + +on: + issue_comment: + types: + - created + +permissions: + pull-requests: write + +jobs: + tc-acion-agent: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + + - name: TC Action Agent + uses: topcoder-platform/tc-action-agent + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # The GITHUB_TOKEN is there by default so you just need to keep it like it is and not necessarily need to add it as secret as it will throw an error. [More Details](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret) From 14711928f6b485e4db01ccb50ad1f4cc0c8b120e Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 10 Jul 2025 11:59:33 +0300 Subject: [PATCH 09/32] fix uses --- .github/workflows/tc_agent.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tc_agent.yml b/.github/workflows/tc_agent.yml index 47cc16f..3dd9d59 100644 --- a/.github/workflows/tc_agent.yml +++ b/.github/workflows/tc_agent.yml @@ -17,6 +17,6 @@ jobs: uses: actions/checkout@v3 - name: TC Action Agent - uses: topcoder-platform/tc-action-agent + uses: topcoder-platform/tc-action-agent@master with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # The GITHUB_TOKEN is there by default so you just need to keep it like it is and not necessarily need to add it as secret as it will throw an error. [More Details](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret) From a52569ad1cb40ada98a1c89b8038c5b5183f177e Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 10 Jul 2025 12:11:32 +0300 Subject: [PATCH 10/32] trigger type --- .github/workflows/tc_agent.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tc_agent.yml b/.github/workflows/tc_agent.yml index 3dd9d59..140dc04 100644 --- a/.github/workflows/tc_agent.yml +++ b/.github/workflows/tc_agent.yml @@ -1,12 +1,8 @@ -name: TC Action Agent +name: TC Action Agent - Do It on: issue_comment: - types: - - created - -permissions: - pull-requests: write + types: [created, edited] jobs: tc-acion-agent: From d7bbbb8379533d7d6183c29f69cc74fbf9f7d009 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 10 Jul 2025 12:30:16 +0300 Subject: [PATCH 11/32] simple trigger on comments --- .github/workflows/tc_agent.yml | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tc_agent.yml b/.github/workflows/tc_agent.yml index 140dc04..705d252 100644 --- a/.github/workflows/tc_agent.yml +++ b/.github/workflows/tc_agent.yml @@ -1,18 +1,26 @@ name: TC Action Agent - Do It -on: - issue_comment: - types: [created, edited] +on: issue_comment jobs: - tc-acion-agent: + pr_commented: + # This job only runs for pull request comments + name: TC Action Agent - Do It - PR comment + if: ${{ github.event.issue.pull_request }} runs-on: ubuntu-latest - steps: - - name: Checkout Repo - uses: actions/checkout@v3 + - run: | + echo A comment on PR $NUMBER + env: + NUMBER: ${{ github.event.issue.number }} - - name: TC Action Agent - uses: topcoder-platform/tc-action-agent@master - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # The GITHUB_TOKEN is there by default so you just need to keep it like it is and not necessarily need to add it as secret as it will throw an error. [More Details](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret) + issue_commented: + # This job only runs for issue comments + name: TC Action Agent - Do It - Issue comment + if: ${{ !github.event.issue.pull_request }} + runs-on: ubuntu-latest + steps: + - run: | + echo A comment on issue $NUMBER + env: + NUMBER: ${{ github.event.issue.number }} From 32faaea4484a6a239636196fe46ed064e6754465 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 10 Jul 2025 13:08:20 +0300 Subject: [PATCH 12/32] call Neo --- .github/workflows/tc_agent.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tc_agent.yml b/.github/workflows/tc_agent.yml index 705d252..a1ea2de 100644 --- a/.github/workflows/tc_agent.yml +++ b/.github/workflows/tc_agent.yml @@ -5,8 +5,8 @@ on: issue_comment jobs: pr_commented: # This job only runs for pull request comments - name: TC Action Agent - Do It - PR comment - if: ${{ github.event.issue.pull_request }} + name: '[PR Comment] - Use TC AI Agent' + if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '@tc-ai') }} runs-on: ubuntu-latest steps: - run: | @@ -14,13 +14,13 @@ jobs: env: NUMBER: ${{ github.event.issue.number }} - issue_commented: - # This job only runs for issue comments - name: TC Action Agent - Do It - Issue comment - if: ${{ !github.event.issue.pull_request }} + tc_agent_tagged_in_issue_comment: + # This job only runs for issue comments where the agent is tagged with @tc-ai + name: '[Issue Comment] - Use TC AI Agent' + if: ${{ !github.event.issue.pull_request && contains(github.event.comment.body, '@tc-ai') }} runs-on: ubuntu-latest steps: - - run: | - echo A comment on issue $NUMBER - env: - NUMBER: ${{ github.event.issue.number }} + - name: Call TC AI Agent + uses: topcoder-platform/tc-action-agent@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} From f802d41842a0e0286330c7b59fa7623d698653a3 Mon Sep 17 00:00:00 2001 From: Vasilica Olariu Date: Thu, 10 Jul 2025 17:02:06 +0300 Subject: [PATCH 13/32] logger level --- src/shared/topcoder/challenges.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shared/topcoder/challenges.service.ts b/src/shared/topcoder/challenges.service.ts index 3e30272..ed294a9 100644 --- a/src/shared/topcoder/challenges.service.ts +++ b/src/shared/topcoder/challenges.service.ts @@ -24,7 +24,7 @@ export class TopcoderChallengesService { } }); - this.logger.debug(`Fetching challenges from: ${url.toString()}`); + this.logger.log(`Fetching challenges from: ${url.toString()}`); const headers: Record = { 'Content-Type': 'application/json', @@ -34,7 +34,7 @@ export class TopcoderChallengesService { headers['Authorization'] = `Bearer ${accessToken}`; } - this.logger.debug( + this.logger.log( `Fetching challenges with headers: ${JSON.stringify(headers)}`, ); From 298e0b5c28e58126c299ca5cd765894aaae75c19 Mon Sep 17 00:00:00 2001 From: Vasilica Olariu Date: Thu, 10 Jul 2025 17:31:37 +0300 Subject: [PATCH 14/32] test log --- src/mcp/tools/challenges/queryChallenges.tool.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mcp/tools/challenges/queryChallenges.tool.ts b/src/mcp/tools/challenges/queryChallenges.tool.ts index ed55795..9c980d9 100644 --- a/src/mcp/tools/challenges/queryChallenges.tool.ts +++ b/src/mcp/tools/challenges/queryChallenges.tool.ts @@ -54,6 +54,11 @@ export class QueryChallengesTool { this.logger.error( `Failed to fetch challenges from Topcoder API: ${challenges.statusText}`, ); + try { + this.logger.error(await challenges.json()); + } catch (e) { + this.logger.error('Failed to log challenge error'); + } // Return an error response if the API call fails return { @@ -108,7 +113,7 @@ export class QueryChallengesTool { }, }; } catch (error) { - this.logger.error(`Error fetching challenges: ${error.message}`); + this.logger.error(`Error fetching challenges: ${error.message}`, error); return { content: [ { From 8491fead7a12b6eab4fce2554cbc4788c2f4f165 Mon Sep 17 00:00:00 2001 From: Vasilica Olariu Date: Thu, 10 Jul 2025 19:00:57 +0300 Subject: [PATCH 15/32] more logs --- src/shared/topcoder/challenges.service.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/shared/topcoder/challenges.service.ts b/src/shared/topcoder/challenges.service.ts index ed294a9..372d829 100644 --- a/src/shared/topcoder/challenges.service.ts +++ b/src/shared/topcoder/challenges.service.ts @@ -24,7 +24,8 @@ export class TopcoderChallengesService { } }); - this.logger.log(`Fetching challenges from: ${url.toString()}`); + const stringUrl = url.toString(); + this.logger.log(`Fetching challenges from: "${stringUrl}"`); const headers: Record = { 'Content-Type': 'application/json', @@ -35,12 +36,17 @@ export class TopcoderChallengesService { } this.logger.log( - `Fetching challenges with headers: ${JSON.stringify(headers)}`, + `Fetching challenges with headers: "${JSON.stringify(headers)}"`, ); - return fetch(url.toString(), { - method: 'GET', - headers, - }); + try { + return await fetch(stringUrl, { + method: 'GET', + headers, + }); + } catch (error) { + this.logger.error(`Error fetching challenges: ${JSON.stringify(error)}`, error); + throw error; + } } } From ba7e6eed7cc88b27cde911cae1eb2461058289c1 Mon Sep 17 00:00:00 2001 From: Vasilica Olariu Date: Thu, 10 Jul 2025 19:27:26 +0300 Subject: [PATCH 16/32] Use axios for fetching challenges --- .../tools/challenges/queryChallenges.tool.ts | 68 +++++++++---------- src/shared/topcoder/challenges.service.ts | 7 +- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/mcp/tools/challenges/queryChallenges.tool.ts b/src/mcp/tools/challenges/queryChallenges.tool.ts index 9c980d9..b5f3ec9 100644 --- a/src/mcp/tools/challenges/queryChallenges.tool.ts +++ b/src/mcp/tools/challenges/queryChallenges.tool.ts @@ -50,12 +50,12 @@ export class QueryChallengesTool { accessToken, ); - if (!challenges.ok) { + if (challenges.status < 200 || challenges.status >= 300) { this.logger.error( `Failed to fetch challenges from Topcoder API: ${challenges.statusText}`, ); try { - this.logger.error(await challenges.json()); + this.logger.error(challenges.data); } catch (e) { this.logger.error('Failed to log challenge error'); } @@ -63,53 +63,53 @@ export class QueryChallengesTool { // Return an error response if the API call fails return { content: [ - { - type: 'text', - text: `Error fetching challenges: ${challenges.statusText}`, - }, + { + type: 'text', + text: `Error fetching challenges: ${challenges.statusText}`, + }, ], isError: true, }; } - // Parse the response as JSON - const challengesData = await challenges.json(); + // Axios response: data is already parsed, headers are plain object + const challengesData = challenges.data; return { content: [ { - type: 'text', - text: JSON.stringify({ - page: Number(challenges.headers.get('x-page')) || 1, - pageSize: - Number(challenges.headers.get('x-per-page')) || - challengesData.length || - 0, - total: - Number(challenges.headers.get('x-total')) || - challengesData.length || - 0, - nextPage: challenges.headers.get('x-next-page') - ? Number(challenges.headers.get('x-next-page')) - : null, - data: challengesData, - }), - }, - ], - structuredContent: { - page: Number(challenges.headers.get('x-page')) || 1, + type: 'text', + text: JSON.stringify({ + page: Number(challenges.headers['x-page']) || 1, pageSize: - Number(challenges.headers.get('x-per-page')) || - challengesData.length || + Number(challenges.headers['x-per-page']) || + (Array.isArray(challengesData) ? challengesData.length : 0) || 0, total: - Number(challenges.headers.get('x-total')) || - challengesData.length || + Number(challenges.headers['x-total']) || + (Array.isArray(challengesData) ? challengesData.length : 0) || 0, - nextPage: challenges.headers.get('x-next-page') - ? Number(challenges.headers.get('x-next-page')) + nextPage: challenges.headers['x-next-page'] + ? Number(challenges.headers['x-next-page']) : null, data: challengesData, + }), + }, + ], + structuredContent: { + page: Number(challenges.headers['x-page']) || 1, + pageSize: + Number(challenges.headers['x-per-page']) || + (Array.isArray(challengesData) ? challengesData.length : 0) || + 0, + total: + Number(challenges.headers['x-total']) || + (Array.isArray(challengesData) ? challengesData.length : 0) || + 0, + nextPage: challenges.headers['x-next-page'] + ? Number(challenges.headers['x-next-page']) + : null, + data: challengesData, }, }; } catch (error) { diff --git a/src/shared/topcoder/challenges.service.ts b/src/shared/topcoder/challenges.service.ts index 372d829..1d16171 100644 --- a/src/shared/topcoder/challenges.service.ts +++ b/src/shared/topcoder/challenges.service.ts @@ -3,6 +3,7 @@ import { ENV_CONFIG } from 'src/config'; import { Logger } from 'src/shared/global'; import { QUERY_CHALLENGES_TOOL_PARAMETERS } from 'src/mcp/tools/challenges/queryChallenges.parameters'; import { z } from 'zod'; +import axios from 'axios'; const { TOPCODER_API_BASE_URL } = ENV_CONFIG; @@ -40,10 +41,8 @@ export class TopcoderChallengesService { ); try { - return await fetch(stringUrl, { - method: 'GET', - headers, - }); + const response = await axios.get(stringUrl, { headers }); + return response; } catch (error) { this.logger.error(`Error fetching challenges: ${JSON.stringify(error)}`, error); throw error; From 88d24a3d7e60b4fc874f107a21e13d5d9ec9db1c Mon Sep 17 00:00:00 2001 From: Vasilica Olariu Date: Thu, 10 Jul 2025 19:30:07 +0300 Subject: [PATCH 17/32] package updated - add axios --- package.json | 3 ++- pnpm-lock.yaml | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index a899e47..a725a5a 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "@nestjs/common": "^11.0.1", "@nestjs/core": "^11.0.1", "@nestjs/platform-express": "^11.0.1", + "@tc/mcp-nest": "topcoder-platform/MCP-Nest.git", + "axios": "^1.10.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", "dotenv": "^16.5.0", @@ -33,7 +35,6 @@ "nanoid": "^5.1.5", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", - "@tc/mcp-nest": "topcoder-platform/MCP-Nest.git", "zod": "^3.25.67" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c45a4f1..61bdb60 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: '@tc/mcp-nest': specifier: topcoder-platform/MCP-Nest.git version: https://codeload.github.com/topcoder-platform/MCP-Nest/tar.gz/025395f5f3a665e8439e481f84c25c6e6ed97933(@modelcontextprotocol/sdk@1.13.3)(@nestjs/common@11.1.3(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3)(express@5.1.0)(reflect-metadata@0.2.2)(zod-to-json-schema@3.24.6(zod@3.25.71))(zod@3.25.71) + axios: + specifier: ^1.10.0 + version: 1.10.0 class-transformer: specifier: ^0.5.1 version: 0.5.1 @@ -1352,6 +1355,9 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + axios@1.10.0: + resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==} + b4a@1.6.7: resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} @@ -1989,6 +1995,15 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -2892,6 +2907,9 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -4975,6 +4993,14 @@ snapshots: asynckit@0.4.0: {} + axios@1.10.0: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.3 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + b4a@1.6.7: {} babel-jest@29.7.0(@babel/core@7.28.0): @@ -5666,6 +5692,8 @@ snapshots: flatted@3.3.3: {} + follow-redirects@1.15.9: {} + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -6688,6 +6716,8 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 + proxy-from-env@1.1.0: {} + punycode@2.3.1: {} pure-rand@6.1.0: {} From 088c749898ccf2b3df07a3e3f78c9f2b29a165da Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Wed, 16 Jul 2025 07:19:35 +0300 Subject: [PATCH 18/32] Add swagger API docs as resources --- src/app.module.ts | 11 ++-- src/core/auth/guards/guards.utils.ts | 1 + src/main.ts | 2 +- src/mcp/resources/resources.module.ts | 17 ++++++ .../resources/swagger/challenges.resource.ts | 33 +++++++++++ .../resources/swagger/identity.resource.ts | 33 +++++++++++ src/mcp/resources/swagger/member.resource.ts | 33 +++++++++++ src/mcp/resources/swagger/review.resource.ts | 32 +++++++++++ .../tools/challenges/queryChallenges.tool.ts | 56 +++++++++---------- src/mcp/tools/tools.module.ts | 10 ++++ src/shared/topcoder/challenges.service.ts | 5 +- 11 files changed, 198 insertions(+), 35 deletions(-) create mode 100644 src/mcp/resources/resources.module.ts create mode 100644 src/mcp/resources/swagger/challenges.resource.ts create mode 100644 src/mcp/resources/swagger/identity.resource.ts create mode 100644 src/mcp/resources/swagger/member.resource.ts create mode 100644 src/mcp/resources/swagger/review.resource.ts create mode 100644 src/mcp/tools/tools.module.ts diff --git a/src/app.module.ts b/src/app.module.ts index f5c9db1..65d5ae0 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,11 +1,11 @@ import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; import { McpModule } from '@tc/mcp-nest'; -import { QueryChallengesTool } from './mcp/tools/challenges/queryChallenges.tool'; -import { GlobalProvidersModule } from './shared/global/globalProviders.module'; -import { TopcoderModule } from './shared/topcoder/topcoder.module'; import { HealthCheckController } from './api/health-check/healthCheck.controller'; import { TokenValidatorMiddleware } from './core/auth/middleware/tokenValidator.middleware'; import { nanoid } from 'nanoid'; +import { ToolsModule } from './mcp/tools/tools.module'; +import { GlobalProvidersModule } from './shared/global/globalProviders.module'; +import { ResourcesModule } from './mcp/resources/resources.module'; @Module({ imports: [ @@ -19,10 +19,11 @@ import { nanoid } from 'nanoid'; }, }), GlobalProvidersModule, - TopcoderModule, + ToolsModule, + ResourcesModule, ], controllers: [HealthCheckController], - providers: [QueryChallengesTool], + providers: [], }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { diff --git a/src/core/auth/guards/guards.utils.ts b/src/core/auth/guards/guards.utils.ts index cd83d82..f2045fa 100644 --- a/src/core/auth/guards/guards.utils.ts +++ b/src/core/auth/guards/guards.utils.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-redundant-type-constituents */ import * as jwt from 'jsonwebtoken'; import { Logger } from 'src/shared/global'; import { getSigningKey } from '../jwt'; diff --git a/src/main.ts b/src/main.ts index f50fe2a..5066589 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,7 +5,7 @@ import { ENV_CONFIG } from './config'; async function bootstrap() { const app = await NestFactory.create(AppModule, { - logger: ['error', 'warn', 'log'], + logger: ['error', 'warn', 'log', 'debug'], }); const logger = new Logger('bootstrap()'); diff --git a/src/mcp/resources/resources.module.ts b/src/mcp/resources/resources.module.ts new file mode 100644 index 0000000..2b5c8ad --- /dev/null +++ b/src/mcp/resources/resources.module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; +import { ChallengesApiSwaggerResource } from './swagger/challenges.resource'; +import { MemberApiSwaggerResource } from './swagger/member.resource'; +import { IdentityApiSwaggerResource } from './swagger/identity.resource'; +import { ReviewApiSwaggerResource } from './swagger/review.resource'; + +@Module({ + imports: [], + controllers: [], + providers: [ + ChallengesApiSwaggerResource, + MemberApiSwaggerResource, + IdentityApiSwaggerResource, + ReviewApiSwaggerResource, + ], +}) +export class ResourcesModule {} diff --git a/src/mcp/resources/swagger/challenges.resource.ts b/src/mcp/resources/swagger/challenges.resource.ts new file mode 100644 index 0000000..e72ed3e --- /dev/null +++ b/src/mcp/resources/swagger/challenges.resource.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { Resource } from '@tc/mcp-nest'; +import axios from 'axios'; +import { Logger } from 'src/shared/global'; + +const SPEC_URL = + 'https://raw.githubusercontent.com/topcoder-platform/member-api-v6/refs/heads/develop/docs/swagger.yaml'; + +@Injectable() +export class ChallengesApiSwaggerResource { + private readonly logger = new Logger(ChallengesApiSwaggerResource.name); + + @Resource({ + uri: SPEC_URL, + name: 'Challenges V6 API Swagger', + description: 'Swagger documentation for the Challenges V6 API', + mimeType: 'text/yaml', + }) + async getChallengesApiSwagger() { + this.logger.debug('Fetching Challenges V6 API Swagger'); + // Fetch the content from the URI and return it. + const rawContent = await axios.get(SPEC_URL); + return { + contents: [ + { + uri: SPEC_URL, + mimeType: 'text/yaml', + text: rawContent.data, + }, + ], + }; + } +} diff --git a/src/mcp/resources/swagger/identity.resource.ts b/src/mcp/resources/swagger/identity.resource.ts new file mode 100644 index 0000000..cbaf2ac --- /dev/null +++ b/src/mcp/resources/swagger/identity.resource.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { Resource } from '@tc/mcp-nest'; +import axios from 'axios'; +import { Logger } from 'src/shared/global'; + +const SPEC_URL = + 'https://raw.githubusercontent.com/topcoder-platform/identity-api-v6/refs/heads/develop/doc/swagger.yaml'; + +@Injectable() +export class IdentityApiSwaggerResource { + private readonly logger = new Logger(IdentityApiSwaggerResource.name); + + @Resource({ + uri: SPEC_URL, + name: 'Identity V6 API Swagger', + description: 'Swagger documentation for the Identity V6 API', + mimeType: 'text/yaml', + }) + async getIdentityApiSwagger() { + this.logger.debug('Fetching Identity V6 API Swagger'); + // Fetch the content from the URI and return it. + const rawContent = await axios.get(SPEC_URL); + return { + contents: [ + { + uri: SPEC_URL, + mimeType: 'text/yaml', + text: rawContent.data, + }, + ], + }; + } +} diff --git a/src/mcp/resources/swagger/member.resource.ts b/src/mcp/resources/swagger/member.resource.ts new file mode 100644 index 0000000..b05e47e --- /dev/null +++ b/src/mcp/resources/swagger/member.resource.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { Resource } from '@tc/mcp-nest'; +import axios from 'axios'; +import { Logger } from 'src/shared/global'; + +const SPEC_URL = + 'https://raw.githubusercontent.com/topcoder-platform/member-api-v6/refs/heads/develop/docs/swagger.yaml'; + +@Injectable() +export class MemberApiSwaggerResource { + private readonly logger = new Logger(MemberApiSwaggerResource.name); + + @Resource({ + uri: SPEC_URL, + name: 'Member V6 API Swagger', + description: 'Swagger documentation for the Member V6 API', + mimeType: 'text/yaml', + }) + async getMemberApiSwagger() { + this.logger.debug('Fetching Member V6 API Swagger'); + // Fetch the content from the URI and return it. + const rawContent = await axios.get(SPEC_URL); + return { + contents: [ + { + uri: SPEC_URL, + mimeType: 'text/yaml', + text: rawContent.data, + }, + ], + }; + } +} diff --git a/src/mcp/resources/swagger/review.resource.ts b/src/mcp/resources/swagger/review.resource.ts new file mode 100644 index 0000000..c301093 --- /dev/null +++ b/src/mcp/resources/swagger/review.resource.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@nestjs/common'; +import { Resource } from '@tc/mcp-nest'; +import axios from 'axios'; +import { Logger } from 'src/shared/global'; + +const SPEC_URL = 'https://api.topcoder-dev.com/v5/review/api-docs-yaml'; + +@Injectable() +export class ReviewApiSwaggerResource { + private readonly logger = new Logger(ReviewApiSwaggerResource.name); + + @Resource({ + uri: SPEC_URL, + name: 'Review V6 API Swagger', + description: 'Swagger documentation for the Review V6 API', + mimeType: 'text/yaml', + }) + async getReviewApiSwagger() { + this.logger.debug('Fetching Review V6 API Swagger'); + // Fetch the content from the URI and return it. + const rawContent = await axios.get(SPEC_URL); + return { + contents: [ + { + uri: SPEC_URL, + mimeType: 'text/yaml', + text: rawContent.data, + }, + ], + }; + } +} diff --git a/src/mcp/tools/challenges/queryChallenges.tool.ts b/src/mcp/tools/challenges/queryChallenges.tool.ts index b5f3ec9..0098f8f 100644 --- a/src/mcp/tools/challenges/queryChallenges.tool.ts +++ b/src/mcp/tools/challenges/queryChallenges.tool.ts @@ -57,16 +57,16 @@ export class QueryChallengesTool { try { this.logger.error(challenges.data); } catch (e) { - this.logger.error('Failed to log challenge error'); + this.logger.error('Failed to log challenge error', e); } // Return an error response if the API call fails return { content: [ - { - type: 'text', - text: `Error fetching challenges: ${challenges.statusText}`, - }, + { + type: 'text', + text: `Error fetching challenges: ${challenges.statusText}`, + }, ], isError: true, }; @@ -78,8 +78,25 @@ export class QueryChallengesTool { return { content: [ { - type: 'text', - text: JSON.stringify({ + type: 'text', + text: JSON.stringify({ + page: Number(challenges.headers['x-page']) || 1, + pageSize: + Number(challenges.headers['x-per-page']) || + (Array.isArray(challengesData) ? challengesData.length : 0) || + 0, + total: + Number(challenges.headers['x-total']) || + (Array.isArray(challengesData) ? challengesData.length : 0) || + 0, + nextPage: challenges.headers['x-next-page'] + ? Number(challenges.headers['x-next-page']) + : null, + data: challengesData, + }), + }, + ], + structuredContent: { page: Number(challenges.headers['x-page']) || 1, pageSize: Number(challenges.headers['x-per-page']) || @@ -93,23 +110,6 @@ export class QueryChallengesTool { ? Number(challenges.headers['x-next-page']) : null, data: challengesData, - }), - }, - ], - structuredContent: { - page: Number(challenges.headers['x-page']) || 1, - pageSize: - Number(challenges.headers['x-per-page']) || - (Array.isArray(challengesData) ? challengesData.length : 0) || - 0, - total: - Number(challenges.headers['x-total']) || - (Array.isArray(challengesData) ? challengesData.length : 0) || - 0, - nextPage: challenges.headers['x-next-page'] - ? Number(challenges.headers['x-next-page']) - : null, - data: challengesData, }, }; } catch (error) { @@ -129,7 +129,7 @@ export class QueryChallengesTool { @Tool({ name: 'query-tc-challenges-private', description: - 'Returns a list of public Topcoder challenges based on the query parameters.', + 'Returns a list of Topcoder challenges based on the query parameters.', parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, annotations: { @@ -145,7 +145,7 @@ export class QueryChallengesTool { @Tool({ name: 'query-tc-challenges-protected', description: - 'Returns a list of public Topcoder challenges based on the query parameters.', + 'Returns a list of Topcoder challenges based on the query parameters.', parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, annotations: { @@ -161,7 +161,7 @@ export class QueryChallengesTool { @Tool({ name: 'query-tc-challenges-m2m', description: - 'Returns a list of public Topcoder challenges based on the query parameters.', + 'Returns a list of Topcoder challenges based on the query parameters.', parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, annotations: { @@ -185,7 +185,7 @@ export class QueryChallengesTool { readOnlyHint: true, }, }) - async queryChallengesPublic(params) { + async queryChallenges(params) { return this._queryChallenges(params); } } diff --git a/src/mcp/tools/tools.module.ts b/src/mcp/tools/tools.module.ts new file mode 100644 index 0000000..7405d4b --- /dev/null +++ b/src/mcp/tools/tools.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { QueryChallengesTool } from './challenges/queryChallenges.tool'; +import { TopcoderModule } from 'src/shared/topcoder/topcoder.module'; + +@Module({ + imports: [TopcoderModule], + controllers: [], + providers: [QueryChallengesTool], +}) +export class ToolsModule {} diff --git a/src/shared/topcoder/challenges.service.ts b/src/shared/topcoder/challenges.service.ts index 1d16171..d4601ba 100644 --- a/src/shared/topcoder/challenges.service.ts +++ b/src/shared/topcoder/challenges.service.ts @@ -44,7 +44,10 @@ export class TopcoderChallengesService { const response = await axios.get(stringUrl, { headers }); return response; } catch (error) { - this.logger.error(`Error fetching challenges: ${JSON.stringify(error)}`, error); + this.logger.error( + `Error fetching challenges: ${JSON.stringify(error)}`, + error, + ); throw error; } } From 10ddf0fd1125648247e34b47c419718af2e8cc08 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Wed, 16 Jul 2025 08:36:58 +0300 Subject: [PATCH 19/32] add skills tool & clean up --- README.md | 55 ++++++++ .../tools/challenges/queryChallenges.tool.ts | 58 +------- src/mcp/tools/skills/querySkills.output.ts | 36 +++++ .../tools/skills/querySkills.parameters.ts | 30 ++++ src/mcp/tools/skills/querySkills.tool.ts | 131 ++++++++++++++++++ src/mcp/tools/tools.module.ts | 3 +- src/shared/topcoder/skills.service.ts | 57 ++++++++ src/shared/topcoder/topcoder.module.ts | 5 +- 8 files changed, 316 insertions(+), 59 deletions(-) create mode 100644 src/mcp/tools/skills/querySkills.output.ts create mode 100644 src/mcp/tools/skills/querySkills.parameters.ts create mode 100644 src/mcp/tools/skills/querySkills.tool.ts create mode 100644 src/shared/topcoder/skills.service.ts diff --git a/README.md b/README.md index 06dcc8f..49d81b4 100644 --- a/README.md +++ b/README.md @@ -1 +1,56 @@ # Topcoder Model Context Protocol (MCP) Server + +## Authentication Based Access via Guards + +Tools/Resources/Prompts support authentication via TC JWT and/or M2M JWT. Providing JWT in the requests to the MCP server will result in specific listings and bahavior based on JWT access level/roles/permissions. + +#### Using `authGuard` - requires TC jwt presence for access + +```ts + @Tool({ + name: 'query-tc-challenges-private', + description: + 'Returns a list of Topcoder challenges based on the query parameters.', + parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, + outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, + annotations: { + title: 'Query Public Topcoder Challenges', + readOnlyHint: true, + }, + canActivate: authGuard, + }) +``` + +#### Using `checkHasUserRole(Role.Admin)` - TC Role based guard + +```ts + @Tool({ + name: 'query-tc-challenges-protected', + description: + 'Returns a list of Topcoder challenges based on the query parameters.', + parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, + outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, + annotations: { + title: 'Query Public Topcoder Challenges', + readOnlyHint: true, + }, + canActivate: checkHasUserRole(Role.Admin), + }) +``` + +#### Using `canActivate: checkM2MScope(M2mScope.QueryPublicChallenges)` - M2M based access via scopes + +```ts + @Tool({ + name: 'query-tc-challenges-m2m', + description: + 'Returns a list of Topcoder challenges based on the query parameters.', + parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, + outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, + annotations: { + title: 'Query Public Topcoder Challenges', + readOnlyHint: true, + }, + canActivate: checkM2MScope(M2mScope.QueryPublicChallenges), + }) +``` diff --git a/src/mcp/tools/challenges/queryChallenges.tool.ts b/src/mcp/tools/challenges/queryChallenges.tool.ts index 0098f8f..7fbd475 100644 --- a/src/mcp/tools/challenges/queryChallenges.tool.ts +++ b/src/mcp/tools/challenges/queryChallenges.tool.ts @@ -5,12 +5,6 @@ import { QUERY_CHALLENGES_TOOL_PARAMETERS } from './queryChallenges.parameters'; import { TopcoderChallengesService } from 'src/shared/topcoder/challenges.service'; import { Logger } from 'src/shared/global'; import { QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA } from './queryChallenges.output'; -import { - authGuard, - checkHasUserRole, - checkM2MScope, -} from 'src/core/auth/guards'; -import { M2mScope, Role } from 'src/core/auth/auth.constants'; @Injectable() export class QueryChallengesTool { @@ -127,61 +121,13 @@ export class QueryChallengesTool { } @Tool({ - name: 'query-tc-challenges-private', + name: 'query-tc-challenges', description: 'Returns a list of Topcoder challenges based on the query parameters.', parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, annotations: { - title: 'Query Public Topcoder Challenges', - readOnlyHint: true, - }, - canActivate: authGuard, - }) - async queryChallengesPrivate(params) { - return this._queryChallenges(params); - } - - @Tool({ - name: 'query-tc-challenges-protected', - description: - 'Returns a list of Topcoder challenges based on the query parameters.', - parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, - outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, - annotations: { - title: 'Query Public Topcoder Challenges', - readOnlyHint: true, - }, - canActivate: checkHasUserRole(Role.Admin), - }) - async queryChallengesProtected(params) { - return this._queryChallenges(params); - } - - @Tool({ - name: 'query-tc-challenges-m2m', - description: - 'Returns a list of Topcoder challenges based on the query parameters.', - parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, - outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, - annotations: { - title: 'Query Public Topcoder Challenges', - readOnlyHint: true, - }, - canActivate: checkM2MScope(M2mScope.QueryPublicChallenges), - }) - async queryChallengesM2m(params) { - return this._queryChallenges(params); - } - - @Tool({ - name: 'query-tc-challenges-public', - description: - 'Returns a list of public Topcoder challenges based on the query parameters.', - parameters: QUERY_CHALLENGES_TOOL_PARAMETERS, - outputSchema: QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA, - annotations: { - title: 'Query Public Topcoder Challenges', + title: 'Query Topcoder Challenges', readOnlyHint: true, }, }) diff --git a/src/mcp/tools/skills/querySkills.output.ts b/src/mcp/tools/skills/querySkills.output.ts new file mode 100644 index 0000000..94403b6 --- /dev/null +++ b/src/mcp/tools/skills/querySkills.output.ts @@ -0,0 +1,36 @@ +import { z } from 'zod'; + +export const QUERY_SKILLS_TOOL_OUTPUT_SCHEMA = z.object({ + page: z.number().describe('Current page number in the paginated response'), + pageSize: z + .number() + .describe( + 'Number of standardized skills per page in the paginated response', + ), + total: z + .number() + .describe('Total number of standardized skills matching the query'), + data: z + .array( + z + .object({ + id: z.string().describe('Unique identifier for the skill'), + name: z.string().describe('Skill name'), + description: z + .string() + .describe('Detailed description of the standardized skill'), + category: z + .object({ + id: z.string().describe('Unique identifier for the category'), + name: z.string().describe('Category name'), + description: z + .string() + .optional() + .describe('Detailed description of the category'), + }) + .describe('Category to which the skill belongs'), + }) + .describe('Standardized skill object'), + ) + .describe("Array of Topcoder's standardized skills"), +}); diff --git a/src/mcp/tools/skills/querySkills.parameters.ts b/src/mcp/tools/skills/querySkills.parameters.ts new file mode 100644 index 0000000..f942de8 --- /dev/null +++ b/src/mcp/tools/skills/querySkills.parameters.ts @@ -0,0 +1,30 @@ +import { z } from 'zod'; + +export const QUERY_SKILLS_TOOL_PARAMETERS = z.object({ + name: z + .array(z.string()) + .optional() + .describe('Filter by skill names, exact match.'), + skillId: z + .array(z.string().uuid()) + .optional() + .describe('Filter by skill IDs, exact match.'), + sortBy: z + .enum(['name', 'description', 'created_at', 'updated_at']) + .optional() + .describe('Sort challenges by a specific field'), + sortOrder: z.enum(['asc', 'desc']).optional().describe('Sort order'), + page: z + .number() + .gte(1) + .default(1) + .optional() + .describe('Page number for pagination, starting from 1'), + perPage: z + .number() + .gte(1) + .lte(100) + .default(20) + .optional() + .describe('Number of standardized skills per page, between 1 and 100'), +}); diff --git a/src/mcp/tools/skills/querySkills.tool.ts b/src/mcp/tools/skills/querySkills.tool.ts new file mode 100644 index 0000000..f4a9524 --- /dev/null +++ b/src/mcp/tools/skills/querySkills.tool.ts @@ -0,0 +1,131 @@ +import { Injectable, Inject } from '@nestjs/common'; +import { Tool } from '@tc/mcp-nest'; +import { REQUEST } from '@nestjs/core'; +import { Logger } from 'src/shared/global'; +import { QUERY_SKILLS_TOOL_PARAMETERS } from './querySkills.parameters'; +import { QUERY_SKILLS_TOOL_OUTPUT_SCHEMA } from './querySkills.output'; +import { TopcoderSkillsService } from 'src/shared/topcoder/skills.service'; + +@Injectable() +export class QuerySkillsTool { + private readonly logger = new Logger(QuerySkillsTool.name); + + constructor( + private readonly topcoderSkillsService: TopcoderSkillsService, + @Inject(REQUEST) private readonly request: any, + ) {} + + private async _querySkills(params) { + // Validate the input parameters + const validatedParams = QUERY_SKILLS_TOOL_PARAMETERS.safeParse(params); + if (!validatedParams.success) { + this.logger.error( + `Invalid parameters provided: ${JSON.stringify(validatedParams.error.errors)}`, + ); + + // Return an error response with the validation errors + return { + content: [ + { + type: 'text', + text: `Invalid parameters: ${JSON.stringify(validatedParams.error.errors)}`, + }, + ], + isError: true, + }; + } + + // Get the challenges from the Topcoder challenges API + // and handle any errors that may occur + try { + const accessToken = this.request.headers['authorization']?.split(' ')[1]; + const skills = await this.topcoderSkillsService.fetchSkills( + validatedParams.data, + accessToken, + ); + + if (skills.status < 200 || skills.status >= 300) { + this.logger.error( + `Failed to fetch skills from Topcoder API: ${skills.statusText}`, + ); + try { + this.logger.error(skills.data); + } catch (e) { + this.logger.error('Failed to log skills error', e); + } + + // Return an error response if the API call fails + return { + content: [ + { + type: 'text', + text: `Error fetching skills: ${skills.statusText}`, + }, + ], + isError: true, + }; + } + + // Axios response: data is already parsed, headers are plain object + const skillsData = skills.data; + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + page: Number(skills.headers['x-page']) || 1, + pageSize: + Number(skills.headers['x-per-page']) || + (Array.isArray(skillsData) ? skillsData.length : 0) || + 0, + total: + Number(skills.headers['x-total']) || + (Array.isArray(skillsData) ? skillsData.length : 0) || + 0, + data: skillsData, + }), + }, + ], + structuredContent: { + page: Number(skills.headers['x-page']) || 1, + pageSize: + Number(skills.headers['x-per-page']) || + (Array.isArray(skillsData) ? skillsData.length : 0) || + 0, + total: + Number(skills.headers['x-total']) || + (Array.isArray(skillsData) ? skillsData.length : 0) || + 0, + data: skillsData, + }, + }; + } catch (error) { + this.logger.error(`Error fetching skills: ${error.message}`, error); + return { + content: [ + { + type: 'text', + text: `Error fetching skills: ${error.message}`, + }, + ], + isError: true, + }; + } + } + + @Tool({ + name: 'query-tc-skills', + description: + 'Returns a list of standardized skills from Topcoder platform, filtered and sorted based on the provided parameters.', + parameters: QUERY_SKILLS_TOOL_PARAMETERS, + outputSchema: QUERY_SKILLS_TOOL_OUTPUT_SCHEMA, + annotations: { + title: 'Query Topcoder Standardized Skills', + readOnlyHint: true, + }, + }) + async querySkills(params) { + return this._querySkills(params); + } +} diff --git a/src/mcp/tools/tools.module.ts b/src/mcp/tools/tools.module.ts index 7405d4b..a5fc55e 100644 --- a/src/mcp/tools/tools.module.ts +++ b/src/mcp/tools/tools.module.ts @@ -1,10 +1,11 @@ import { Module } from '@nestjs/common'; import { QueryChallengesTool } from './challenges/queryChallenges.tool'; import { TopcoderModule } from 'src/shared/topcoder/topcoder.module'; +import { QuerySkillsTool } from './skills/querySkills.tool'; @Module({ imports: [TopcoderModule], controllers: [], - providers: [QueryChallengesTool], + providers: [QueryChallengesTool, QuerySkillsTool], }) export class ToolsModule {} diff --git a/src/shared/topcoder/skills.service.ts b/src/shared/topcoder/skills.service.ts new file mode 100644 index 0000000..16d464c --- /dev/null +++ b/src/shared/topcoder/skills.service.ts @@ -0,0 +1,57 @@ +import { Injectable } from '@nestjs/common'; +import { ENV_CONFIG } from 'src/config'; +import { Logger } from 'src/shared/global'; +import { z } from 'zod'; +import axios from 'axios'; +import { QUERY_SKILLS_TOOL_PARAMETERS } from 'src/mcp/tools/skills/querySkills.parameters'; + +const { TOPCODER_API_BASE_URL } = ENV_CONFIG; + +@Injectable() +export class TopcoderSkillsService { + private readonly logger = new Logger(TopcoderSkillsService.name); + + constructor() {} + + async fetchSkills( + queryParams: z.infer, + accessToken?: string, + ) { + // Format the input parameters + const url = new URL(`${TOPCODER_API_BASE_URL}/standardized-skills/skills`); + Object.entries(queryParams).forEach(([key, value]) => { + if (value !== undefined && value !== null) { + if (Array.isArray(value)) { + value.forEach((v) => url.searchParams.append(key, v)); + } else { + url.searchParams.append(key, value.toString()); + } + } + }); + + const stringUrl = url.toString(); + this.logger.log(`Fetching standardized skills from: "${stringUrl}"`); + + const headers: Record = { + 'Content-Type': 'application/json', + }; + if (accessToken) { + headers['Authorization'] = `Bearer ${accessToken}`; + } + + this.logger.log( + `Fetching standardized skills with headers: "${JSON.stringify(headers)}"`, + ); + + try { + const response = await axios.get(stringUrl, { headers }); + return response; + } catch (error) { + this.logger.error( + `Error fetching challenges: ${JSON.stringify(error)}`, + error, + ); + throw error; + } + } +} diff --git a/src/shared/topcoder/topcoder.module.ts b/src/shared/topcoder/topcoder.module.ts index 4facc65..997146c 100644 --- a/src/shared/topcoder/topcoder.module.ts +++ b/src/shared/topcoder/topcoder.module.ts @@ -1,8 +1,9 @@ import { Module } from '@nestjs/common'; import { TopcoderChallengesService } from './challenges.service'; +import { TopcoderSkillsService } from './skills.service'; @Module({ - providers: [TopcoderChallengesService], - exports: [TopcoderChallengesService], + providers: [TopcoderChallengesService, TopcoderSkillsService], + exports: [TopcoderChallengesService, TopcoderSkillsService], }) export class TopcoderModule {} From a6b38848907c3b4461beb042b8633b1d8925433e Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Wed, 16 Jul 2025 09:30:20 +0300 Subject: [PATCH 20/32] fix challenges v6 swagger url --- src/mcp/resources/swagger/challenges.resource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcp/resources/swagger/challenges.resource.ts b/src/mcp/resources/swagger/challenges.resource.ts index e72ed3e..8e4ed6e 100644 --- a/src/mcp/resources/swagger/challenges.resource.ts +++ b/src/mcp/resources/swagger/challenges.resource.ts @@ -4,7 +4,7 @@ import axios from 'axios'; import { Logger } from 'src/shared/global'; const SPEC_URL = - 'https://raw.githubusercontent.com/topcoder-platform/member-api-v6/refs/heads/develop/docs/swagger.yaml'; + 'https://raw.githubusercontent.com/topcoder-platform/challenge-api-v6/refs/heads/develop/docs/swagger.yaml'; @Injectable() export class ChallengesApiSwaggerResource { From f69d9e52a6799e7f6e501400aaf82369903dfa06 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Wed, 16 Jul 2025 09:35:21 +0300 Subject: [PATCH 21/32] support array in queries for challenegs --- src/mcp/tools/challenges/queryChallenges.parameters.ts | 6 ++++++ src/shared/topcoder/challenges.service.ts | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/mcp/tools/challenges/queryChallenges.parameters.ts b/src/mcp/tools/challenges/queryChallenges.parameters.ts index c2ae895..cae0f65 100644 --- a/src/mcp/tools/challenges/queryChallenges.parameters.ts +++ b/src/mcp/tools/challenges/queryChallenges.parameters.ts @@ -36,6 +36,12 @@ export const QUERY_CHALLENGES_TOOL_PARAMETERS = z.object({ .describe( 'Filter by tag name, case-insensitive, partial matches are allowed.', ), + tags: z + .array(z.string()) + .optional() + .describe( + 'Filter by multiple tag names, case-insensitive, partial matches are allowed.', + ), search: z .string() .optional() diff --git a/src/shared/topcoder/challenges.service.ts b/src/shared/topcoder/challenges.service.ts index d4601ba..c30cbf1 100644 --- a/src/shared/topcoder/challenges.service.ts +++ b/src/shared/topcoder/challenges.service.ts @@ -21,7 +21,11 @@ export class TopcoderChallengesService { const url = new URL(`${TOPCODER_API_BASE_URL}/challenges`); Object.entries(queryParams).forEach(([key, value]) => { if (value !== undefined && value !== null) { - url.searchParams.append(key, value.toString()); + if (Array.isArray(value)) { + value.forEach((v) => url.searchParams.append(key, v)); + } else { + url.searchParams.append(key, value.toString()); + } } }); From b2e1b253659045978624aa113c86837a5d89211d Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Wed, 16 Jul 2025 18:56:02 +0300 Subject: [PATCH 22/32] update ai agnet action --- .github/workflows/tc_agent.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/tc_agent.yml b/.github/workflows/tc_agent.yml index a1ea2de..8e9c29d 100644 --- a/.github/workflows/tc_agent.yml +++ b/.github/workflows/tc_agent.yml @@ -21,6 +21,4 @@ jobs: runs-on: ubuntu-latest steps: - name: Call TC AI Agent - uses: topcoder-platform/tc-action-agent@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} + uses: topcoder-platform/tc-action-agent@master \ No newline at end of file From 77630ec9a14d1e8999a7b94fd89c63208d42f099 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Mon, 28 Jul 2025 07:49:06 +0300 Subject: [PATCH 23/32] Fix schema issues for challenges query tool --- .../challenges/queryChallenges.output.ts | 179 +++++++++++++++++- 1 file changed, 173 insertions(+), 6 deletions(-) diff --git a/src/mcp/tools/challenges/queryChallenges.output.ts b/src/mcp/tools/challenges/queryChallenges.output.ts index 447d8ea..03009fb 100644 --- a/src/mcp/tools/challenges/queryChallenges.output.ts +++ b/src/mcp/tools/challenges/queryChallenges.output.ts @@ -4,6 +4,7 @@ export const QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA = z.object({ page: z.number().describe('Current page number in the paginated response'), nextPage: z .number() + .nullable() .optional() .describe('Next page number, if available in the paginated response'), pageSize: z @@ -18,6 +19,50 @@ export const QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA = z.object({ name: z.string().describe('Challenge title'), typeId: z.string().describe('Type identifier for the challenge'), trackId: z.string().describe('Track identifier for the challenge'), + legacy: z + .object({ + track: z + .string() + .optional() + .describe('Legacy track identifier for the challenge'), + subTrack: z + .string() + .optional() + .describe('Legacy sub-track identifier for the challenge'), + forumId: z + .number() + .optional() + .describe('Legacy forum ID for the challenge'), + directProjectId: z + .number() + .optional() + .describe('Legacy direct project ID for the challenge'), + reviewType: z + .string() + .optional() + .describe('Legacy review type for the challenge'), + confidentialityType: z + .string() + .optional() + .describe('Legacy confidentiality type for the challenge'), + pureV5Task: z + .boolean() + .optional() + .describe('Legacy pure V5 task flag for the challenge'), + reviewScorecardId: z + .number() + .optional() + .describe('Legacy review scorecard ID for the challenge'), + screeningScorecardId: z + .number() + .optional() + .describe('Legacy screening scorecard ID for the challenge'), + selfService: z + .boolean() + .optional() + .describe('Legacy self-service flag for the challenge'), + }) + .optional(), description: z .string() .describe('Detailed description of the challenge'), @@ -25,10 +70,14 @@ export const QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA = z.object({ .string() .describe('Format of the description, e.g., markdown'), metadata: z - .object({ - // Additional metadata fields can be added here - // For example, you can include fields like 'clientId', 'prize', etc - }) + .array( + z.object({ + // Additional metadata fields can be added here + // For example, you can include fields like 'clientId', 'prize', etc + name: z.string().describe('Metadata name'), + value: z.string().describe('Metadata value'), + }), + ) .optional() .describe('Optional metadata associated with the challenge'), timelineTemplateId: z @@ -56,10 +105,30 @@ export const QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA = z.object({ .array( z.object({ name: z.string().describe('Constraint name'), - value: z.string().describe('Constraint value'), + value: z.number().describe('Constraint value'), }), ) .describe('Constraints for the challenge phase'), + actualStartDate: z + .string() + .optional() + .describe( + 'Actual start date of the phase (ISO format, optional)', + ), + description: z + .string() + .optional() + .describe('Description of the phase (optional)'), + predecessor: z + .string() + .optional() + .describe('Identifier of the predecessor phase (optional)'), + actualEndDate: z + .string() + .optional() + .describe( + 'Actual end date of the phase (ISO format, optional)', + ), }), ) .describe('Challenge phases (optional)'), @@ -114,6 +183,10 @@ export const QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA = z.object({ 'Cancelled - Zero Registrations', ]) .describe('Current status of the challenge'), + attachments: z + .array(z.object({}).optional()) + .optional() + .describe('Attachments associated with the challenge (optional)'), track: z .string() .describe('Challenge track (e.g., DEVELOPMENT, DESIGN)'), @@ -131,7 +204,7 @@ export const QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA = z.object({ overview: z .object({ totalPrizes: z.number().describe('Total prize amount'), - types: z.string().describe('Challenge prizes currency'), + type: z.string().optional().describe('Challenge prizes currency'), }) .describe('Overview of the challenge'), skills: z @@ -174,6 +247,100 @@ export const QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA = z.object({ ) .optional() .describe('Array of winners for the challenge (optional)'), + createdBy: z.string().describe('User handle of the creator'), + updatedBy: z + .string() + .optional() + .describe('User handle of the last updater'), + currentPhase: z + .object({ + id: z.string().describe('Current phase unique identifier'), + phaseId: z.string().describe('Current phase identifier'), + name: z.string().describe('Current phase name'), + duration: z + .number() + .describe('Current phase duration in seconds'), + scheduledStartDate: z + .string() + .describe( + 'Scheduled start date of the current phase (ISO format)', + ), + scheduledEndDate: z + .string() + .describe( + 'Scheduled end date of the current phase (ISO format)', + ), + isOpen: z + .boolean() + .describe('Indicates if the current phase is open'), + constraints: z + .array( + z.object({ + name: z.string().describe('Constraint name'), + value: z.number().describe('Constraint value'), + }), + ) + .describe('Constraints for the current phase'), + description: z + .string() + .optional() + .describe('Description of the current phase (optional)'), + actualStartDate: z + .string() + .optional() + .describe( + 'Actual start date of the current phase (ISO format)', + ), + predecessor: z + .string() + .optional() + .describe('Identifier of the predecessor phase (optional)'), + }) + .optional() + .describe('Current phase of the challenge (optional)'), + currentPhaseNames: z + .array(z.string()) + .optional() + .describe( + 'Names of the current phases of the challenge (optional)', + ), + discussions: z + .array( + z.object({ + id: z + .string() + .optional() + .describe('Discussion unique identifier'), + name: z.string().optional().describe('Discussion name'), + type: z.string().optional().describe('Discussion type'), + provider: z.string().optional().describe('Discussion provider'), + url: z.string().optional().describe('URL of the discussion'), + }), + ) + .optional() + .describe('Discussions associated with the challenge (optional)'), + events: z + .array(z.object({})) + .optional() + .describe('Events associated with the challenge (optional)'), + groups: z + .array(z.object({})) + .optional() + .describe('Groups associated with the challenge (optional)'), + legacyId: z + .number() + .optional() + .describe('Legacy identifier for the challenge (optional)'), + projectId: z + .number() + .optional() + .describe('Project identifier for the challenge (optional)'), + numOfCheckpointSubmissions: z + .number() + .optional() + .describe( + 'Number of checkpoint submissions for the challenge (optional)', + ), }) .describe('Challenge object'), ) From 763d7df1ca9e97b8b6a0ccfb5293d081f6b2dd8e Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Mon, 28 Jul 2025 15:54:17 +0300 Subject: [PATCH 24/32] make desc in category nullable --- src/mcp/tools/skills/querySkills.output.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mcp/tools/skills/querySkills.output.ts b/src/mcp/tools/skills/querySkills.output.ts index 94403b6..5087276 100644 --- a/src/mcp/tools/skills/querySkills.output.ts +++ b/src/mcp/tools/skills/querySkills.output.ts @@ -25,6 +25,7 @@ export const QUERY_SKILLS_TOOL_OUTPUT_SCHEMA = z.object({ name: z.string().describe('Category name'), description: z .string() + .nullable() .optional() .describe('Detailed description of the category'), }) From 99a1c1914d00c9b940a624d68ba9b7efe4daf34c Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Mon, 28 Jul 2025 16:00:09 +0300 Subject: [PATCH 25/32] fix skills sort order errors --- src/mcp/tools/skills/querySkills.parameters.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mcp/tools/skills/querySkills.parameters.ts b/src/mcp/tools/skills/querySkills.parameters.ts index f942de8..ddafeae 100644 --- a/src/mcp/tools/skills/querySkills.parameters.ts +++ b/src/mcp/tools/skills/querySkills.parameters.ts @@ -12,8 +12,8 @@ export const QUERY_SKILLS_TOOL_PARAMETERS = z.object({ sortBy: z .enum(['name', 'description', 'created_at', 'updated_at']) .optional() - .describe('Sort challenges by a specific field'), - sortOrder: z.enum(['asc', 'desc']).optional().describe('Sort order'), + .describe('Sort skills by a specific field'), + sortOrder: z.enum(['ASC', 'DESC']).optional().describe('Sort order'), page: z .number() .gte(1) From 98cc27a663988c0fee062605b4c56f0ea25dd661 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Fri, 1 Aug 2025 10:24:45 +0300 Subject: [PATCH 26/32] more output defs --- .../challenges/queryChallenges.output.ts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/mcp/tools/challenges/queryChallenges.output.ts b/src/mcp/tools/challenges/queryChallenges.output.ts index 03009fb..3c6feaa 100644 --- a/src/mcp/tools/challenges/queryChallenges.output.ts +++ b/src/mcp/tools/challenges/queryChallenges.output.ts @@ -241,7 +241,7 @@ export const QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA = z.object({ handle: z .string() .describe('Winner handle on Topcoder platform'), - userId: z.string().describe('Unique identifier for the user'), + userId: z.number().describe('Unique identifier for the user'), placement: z.number().describe('Placement of the winner'), }), ) @@ -295,6 +295,12 @@ export const QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA = z.object({ .string() .optional() .describe('Identifier of the predecessor phase (optional)'), + additionalProperties: z + .object({}) + .optional() + .describe( + 'Additional properties for the current phase (optional)', + ), }) .optional() .describe('Current phase of the challenge (optional)'), @@ -341,6 +347,20 @@ export const QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA = z.object({ .describe( 'Number of checkpoint submissions for the challenge (optional)', ), + task: z + .object({ + isTask: z + .boolean() + .describe('Indicates if the challenge is a task'), + isAssigned: z + .boolean() + .describe('Indicates if the task is assigned'), + memberId: z + .number() + .describe('Member ID of the assigned user (optional)'), + }) + .optional() + .describe('Task information for the challenge (optional)'), }) .describe('Challenge object'), ) From d4e5b06218633a9c60591c434f898405d3459044 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Fri, 1 Aug 2025 10:33:43 +0300 Subject: [PATCH 27/32] fix current phase structure --- src/mcp/tools/challenges/queryChallenges.output.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mcp/tools/challenges/queryChallenges.output.ts b/src/mcp/tools/challenges/queryChallenges.output.ts index 3c6feaa..fd4672c 100644 --- a/src/mcp/tools/challenges/queryChallenges.output.ts +++ b/src/mcp/tools/challenges/queryChallenges.output.ts @@ -295,11 +295,11 @@ export const QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA = z.object({ .string() .optional() .describe('Identifier of the predecessor phase (optional)'), - additionalProperties: z - .object({}) + actualEndDate: z + .string() .optional() .describe( - 'Additional properties for the current phase (optional)', + 'Actual end date of the current phase (ISO format, optional)', ), }) .optional() From 37466ca31bca84d3e2be82d9da48dec2b04ccbd1 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Mon, 4 Aug 2025 05:57:44 +0300 Subject: [PATCH 28/32] add totalPrizesInCents --- src/mcp/tools/challenges/queryChallenges.output.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mcp/tools/challenges/queryChallenges.output.ts b/src/mcp/tools/challenges/queryChallenges.output.ts index fd4672c..a7ee228 100644 --- a/src/mcp/tools/challenges/queryChallenges.output.ts +++ b/src/mcp/tools/challenges/queryChallenges.output.ts @@ -205,6 +205,10 @@ export const QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA = z.object({ .object({ totalPrizes: z.number().describe('Total prize amount'), type: z.string().optional().describe('Challenge prizes currency'), + totalPrizesInCents: z + .number() + .optional() + .describe('Total prize amount in cents'), }) .describe('Overview of the challenge'), skills: z From 3e873fd8f358d7d465047cbd51f68e6b74977e6c Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Mon, 4 Aug 2025 14:23:46 +0800 Subject: [PATCH 29/32] fix: allow passing single tag in "tags" param --- src/shared/topcoder/challenges.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/topcoder/challenges.service.ts b/src/shared/topcoder/challenges.service.ts index c30cbf1..92489ab 100644 --- a/src/shared/topcoder/challenges.service.ts +++ b/src/shared/topcoder/challenges.service.ts @@ -22,7 +22,7 @@ export class TopcoderChallengesService { Object.entries(queryParams).forEach(([key, value]) => { if (value !== undefined && value !== null) { if (Array.isArray(value)) { - value.forEach((v) => url.searchParams.append(key, v)); + value.forEach((v) => url.searchParams.append(`${key}[]`, v)); } else { url.searchParams.append(key, value.toString()); } From c74c69f4f5d85dd933b891fd730ae34b0302d885 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Mon, 18 Aug 2025 10:58:40 +0300 Subject: [PATCH 30/32] up to v6 fore review api --- src/mcp/resources/swagger/review.resource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcp/resources/swagger/review.resource.ts b/src/mcp/resources/swagger/review.resource.ts index c301093..1e27e4b 100644 --- a/src/mcp/resources/swagger/review.resource.ts +++ b/src/mcp/resources/swagger/review.resource.ts @@ -3,7 +3,7 @@ import { Resource } from '@tc/mcp-nest'; import axios from 'axios'; import { Logger } from 'src/shared/global'; -const SPEC_URL = 'https://api.topcoder-dev.com/v5/review/api-docs-yaml'; +const SPEC_URL = 'https://api.topcoder-dev.com/v6/review/api-docs-yaml'; @Injectable() export class ReviewApiSwaggerResource { From 6e610fb40663e37b206928229f3043f364bc3543 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 21 Aug 2025 08:59:42 +0300 Subject: [PATCH 31/32] switch to UUID --- package.json | 1 - pnpm-lock.yaml | 10 ---------- src/app.module.ts | 4 ++-- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index a725a5a..692a379 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "json-stringify-safe": "^5.0.1", "jsonwebtoken": "^9.0.2", "jwks-rsa": "^3.2.0", - "nanoid": "^5.1.5", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "zod": "^3.25.67" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 61bdb60..70a2ae8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,9 +44,6 @@ importers: jwks-rsa: specifier: ^3.2.0 version: 3.2.0 - nanoid: - specifier: ^5.1.5 - version: 5.1.5 reflect-metadata: specifier: ^0.2.2 version: 0.2.2 @@ -2709,11 +2706,6 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} - nanoid@5.1.5: - resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} - engines: {node: ^18 || >=20} - hasBin: true - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -6551,8 +6543,6 @@ snapshots: mute-stream@2.0.0: {} - nanoid@5.1.5: {} - natural-compare@1.4.0: {} negotiator@1.0.0: {} diff --git a/src/app.module.ts b/src/app.module.ts index 65d5ae0..d65b38f 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -2,10 +2,10 @@ import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; import { McpModule } from '@tc/mcp-nest'; import { HealthCheckController } from './api/health-check/healthCheck.controller'; import { TokenValidatorMiddleware } from './core/auth/middleware/tokenValidator.middleware'; -import { nanoid } from 'nanoid'; import { ToolsModule } from './mcp/tools/tools.module'; import { GlobalProvidersModule } from './shared/global/globalProviders.module'; import { ResourcesModule } from './mcp/resources/resources.module'; +import { randomUUID } from 'crypto'; @Module({ imports: [ @@ -14,7 +14,7 @@ import { ResourcesModule } from './mcp/resources/resources.module'; version: '1.0.0', streamableHttp: { enableJsonResponse: false, - sessionIdGenerator: () => nanoid(), + sessionIdGenerator: () => randomUUID(), statelessMode: false, }, }), From 67255d9c5df3cef5e9670c314cd506ca71226d65 Mon Sep 17 00:00:00 2001 From: Vasilica Olariu Date: Wed, 27 Aug 2025 11:54:30 +0300 Subject: [PATCH 32/32] Add timing logging for every call on mcp --- src/app.module.ts | 2 ++ .../resources/swagger/challenges.resource.ts | 2 ++ .../resources/swagger/identity.resource.ts | 2 ++ src/mcp/resources/swagger/member.resource.ts | 2 ++ src/mcp/resources/swagger/review.resource.ts | 2 ++ .../tools/challenges/queryChallenges.tool.ts | 2 ++ src/mcp/tools/skills/querySkills.tool.ts | 2 ++ src/shared/global/logTime.decorator.ts | 32 +++++++++++++++++++ src/shared/global/timingInterceptor.ts | 25 +++++++++++++++ 9 files changed, 71 insertions(+) create mode 100644 src/shared/global/logTime.decorator.ts create mode 100644 src/shared/global/timingInterceptor.ts diff --git a/src/app.module.ts b/src/app.module.ts index d65b38f..9214a26 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -6,6 +6,7 @@ import { ToolsModule } from './mcp/tools/tools.module'; import { GlobalProvidersModule } from './shared/global/globalProviders.module'; import { ResourcesModule } from './mcp/resources/resources.module'; import { randomUUID } from 'crypto'; +import { TimingInterceptorMiddleware } from './shared/global/timingInterceptor'; @Module({ imports: [ @@ -28,5 +29,6 @@ import { randomUUID } from 'crypto'; export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer.apply(TokenValidatorMiddleware).forRoutes('*'); + consumer.apply(TimingInterceptorMiddleware).forRoutes('*'); } } diff --git a/src/mcp/resources/swagger/challenges.resource.ts b/src/mcp/resources/swagger/challenges.resource.ts index 8e4ed6e..ee1642f 100644 --- a/src/mcp/resources/swagger/challenges.resource.ts +++ b/src/mcp/resources/swagger/challenges.resource.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { Resource } from '@tc/mcp-nest'; import axios from 'axios'; import { Logger } from 'src/shared/global'; +import { LogTime } from 'src/shared/global/logTime.decorator'; const SPEC_URL = 'https://raw.githubusercontent.com/topcoder-platform/challenge-api-v6/refs/heads/develop/docs/swagger.yaml'; @@ -16,6 +17,7 @@ export class ChallengesApiSwaggerResource { description: 'Swagger documentation for the Challenges V6 API', mimeType: 'text/yaml', }) + @LogTime('ChallengesApiSwaggerResource') async getChallengesApiSwagger() { this.logger.debug('Fetching Challenges V6 API Swagger'); // Fetch the content from the URI and return it. diff --git a/src/mcp/resources/swagger/identity.resource.ts b/src/mcp/resources/swagger/identity.resource.ts index cbaf2ac..5b9523e 100644 --- a/src/mcp/resources/swagger/identity.resource.ts +++ b/src/mcp/resources/swagger/identity.resource.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { Resource } from '@tc/mcp-nest'; import axios from 'axios'; import { Logger } from 'src/shared/global'; +import { LogTime } from 'src/shared/global/logTime.decorator'; const SPEC_URL = 'https://raw.githubusercontent.com/topcoder-platform/identity-api-v6/refs/heads/develop/doc/swagger.yaml'; @@ -16,6 +17,7 @@ export class IdentityApiSwaggerResource { description: 'Swagger documentation for the Identity V6 API', mimeType: 'text/yaml', }) + @LogTime('IdentityApiSwaggerResource') async getIdentityApiSwagger() { this.logger.debug('Fetching Identity V6 API Swagger'); // Fetch the content from the URI and return it. diff --git a/src/mcp/resources/swagger/member.resource.ts b/src/mcp/resources/swagger/member.resource.ts index b05e47e..fb9fd6a 100644 --- a/src/mcp/resources/swagger/member.resource.ts +++ b/src/mcp/resources/swagger/member.resource.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { Resource } from '@tc/mcp-nest'; import axios from 'axios'; import { Logger } from 'src/shared/global'; +import { LogTime } from 'src/shared/global/logTime.decorator'; const SPEC_URL = 'https://raw.githubusercontent.com/topcoder-platform/member-api-v6/refs/heads/develop/docs/swagger.yaml'; @@ -16,6 +17,7 @@ export class MemberApiSwaggerResource { description: 'Swagger documentation for the Member V6 API', mimeType: 'text/yaml', }) + @LogTime('MemberApiSwaggerResource') async getMemberApiSwagger() { this.logger.debug('Fetching Member V6 API Swagger'); // Fetch the content from the URI and return it. diff --git a/src/mcp/resources/swagger/review.resource.ts b/src/mcp/resources/swagger/review.resource.ts index 1e27e4b..73bcadd 100644 --- a/src/mcp/resources/swagger/review.resource.ts +++ b/src/mcp/resources/swagger/review.resource.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { Resource } from '@tc/mcp-nest'; import axios from 'axios'; import { Logger } from 'src/shared/global'; +import { LogTime } from 'src/shared/global/logTime.decorator'; const SPEC_URL = 'https://api.topcoder-dev.com/v6/review/api-docs-yaml'; @@ -15,6 +16,7 @@ export class ReviewApiSwaggerResource { description: 'Swagger documentation for the Review V6 API', mimeType: 'text/yaml', }) + @LogTime('ReviewApiSwaggerResource') async getReviewApiSwagger() { this.logger.debug('Fetching Review V6 API Swagger'); // Fetch the content from the URI and return it. diff --git a/src/mcp/tools/challenges/queryChallenges.tool.ts b/src/mcp/tools/challenges/queryChallenges.tool.ts index 7fbd475..9dd561d 100644 --- a/src/mcp/tools/challenges/queryChallenges.tool.ts +++ b/src/mcp/tools/challenges/queryChallenges.tool.ts @@ -5,6 +5,7 @@ import { QUERY_CHALLENGES_TOOL_PARAMETERS } from './queryChallenges.parameters'; import { TopcoderChallengesService } from 'src/shared/topcoder/challenges.service'; import { Logger } from 'src/shared/global'; import { QUERY_CHALLENGES_TOOL_OUTPUT_SCHEMA } from './queryChallenges.output'; +import { LogTime } from 'src/shared/global/logTime.decorator'; @Injectable() export class QueryChallengesTool { @@ -131,6 +132,7 @@ export class QueryChallengesTool { readOnlyHint: true, }, }) + @LogTime('ChallengesTool') async queryChallenges(params) { return this._queryChallenges(params); } diff --git a/src/mcp/tools/skills/querySkills.tool.ts b/src/mcp/tools/skills/querySkills.tool.ts index f4a9524..91a0db4 100644 --- a/src/mcp/tools/skills/querySkills.tool.ts +++ b/src/mcp/tools/skills/querySkills.tool.ts @@ -5,6 +5,7 @@ import { Logger } from 'src/shared/global'; import { QUERY_SKILLS_TOOL_PARAMETERS } from './querySkills.parameters'; import { QUERY_SKILLS_TOOL_OUTPUT_SCHEMA } from './querySkills.output'; import { TopcoderSkillsService } from 'src/shared/topcoder/skills.service'; +import { LogTime } from 'src/shared/global/logTime.decorator'; @Injectable() export class QuerySkillsTool { @@ -125,6 +126,7 @@ export class QuerySkillsTool { readOnlyHint: true, }, }) + @LogTime('SkillsTool') async querySkills(params) { return this._querySkills(params); } diff --git a/src/shared/global/logTime.decorator.ts b/src/shared/global/logTime.decorator.ts new file mode 100644 index 0000000..cb478fb --- /dev/null +++ b/src/shared/global/logTime.decorator.ts @@ -0,0 +1,32 @@ +import { Logger } from '@nestjs/common'; + +export function LogTime(label?: string) { + const logger = new Logger('ExecutionTime'); + + return function ( + target: any, + propertyKey: string, + descriptor: PropertyDescriptor, + ) { + const originalMethod = descriptor.value; + + descriptor.value = async function (...args: any[]) { + const start = Date.now(); + + try { + const result = await originalMethod.apply(this, args); + const ms = Date.now() - start; + logger.log(`${label || propertyKey} executed in ${ms}ms`); + return result; + } catch (error) { + const ms = Date.now() - start; + logger.error( + `${label || propertyKey} failed after ${ms}ms – ${error.message}`, + ); + throw error; + } + }; + + return descriptor; + }; +} diff --git a/src/shared/global/timingInterceptor.ts b/src/shared/global/timingInterceptor.ts new file mode 100644 index 0000000..a7756ae --- /dev/null +++ b/src/shared/global/timingInterceptor.ts @@ -0,0 +1,25 @@ +import { Injectable, NestMiddleware, Logger } from '@nestjs/common'; + +import { Request, Response, NextFunction } from 'express'; + +@Injectable() +export class TimingInterceptorMiddleware implements NestMiddleware { + private logger = new Logger('TimingInterceptor'); + + use(request: Request, response: Response, next: NextFunction): void { + const { method, originalUrl: url } = request; + const start = Date.now(); + const mcpMethod = request.body?.method; + + response.on('close', () => { + const { statusCode } = response; + const duration = Date.now() - start; + + this.logger.log( + `${method} ${mcpMethod ? `{${mcpMethod}} ` : ''}${url} ${statusCode} took ${duration}ms` + ); + }); + + next(); + } +} \ No newline at end of file