feat: add @arvoretech/await-until-mcp server#14
Conversation
Polling/waiting primitives for MCP. Four tools: - await_until_command: poll shell commands - await_until_url: poll HTTP endpoints - await_until_file: poll filesystem - await_until_mcp: poll other MCP servers (auto-discovers from mcp.json, supports mcp-proxy upstreams)
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughAdds a new package "@arvoretech/await-until-mcp" providing an MCP server and CLI that poll commands, URLs, files, or other MCP tools until match criteria; includes TypeScript sources, schemas, MCP client helpers, tooling configs, package manifest, and documentation. Also bumps Changes
Sequence DiagramsequenceDiagram
participant Client as External Client
participant Server as AwaitUntilMCPServer
participant Poller as Polling Module
participant Resource as External Resource / MCP
Client->>Server: callTool(toolName, params)
Server->>Poller: dispatch to pollCommand/pollUrl/pollFile/pollMcp
loop until matched or timeout
Poller->>Resource: perform check (run cmd / fetch URL / read file / call MCP)
Resource-->>Poller: return output/status
Poller->>Poller: evaluate match condition
end
Poller-->>Server: return PollResult (success/attempts/elapsed/last_output)
Server-->>Client: formatted tool response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (6)
packages/await-until/eslint.config.js (1)
45-45:no-undefconflicts with TypeScript.With TypeScript,
no-undefis redundant and can cause false positives for type-only imports and TypeScript-specific syntax. The TypeScript compiler already handles undefined variable detection.Consider disabling it or using
@typescript-eslint/no-undefif available.♻️ Disable no-undef for TypeScript
"no-console": "off", - "no-undef": "error", + "no-undef": "off", },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/await-until/eslint.config.js` at line 45, The "no-undef" ESLint rule is conflicting with TypeScript; update the ESLint config to remove or disable "no-undef" and instead rely on TypeScript's checking (or replace it with the TypeScript-aware rule "@typescript-eslint/no-undef" if that plugin is used). Locate the rule entry "no-undef": "error" in the eslint.config.js and either delete it or set it to "off", or enable "@typescript-eslint/no-undef" in rules and ensure "@typescript-eslint" is in the plugins/extends so the TypeScript-aware rule is used.packages/await-until/tsconfig.json (2)
5-5: Consider aligningmoduleResolutionwith root config.The root
tsconfig.jsonuses"moduleResolution": "bundler"which is the recommended setting for modern ESM projects. This package uses"Node", which is inconsistent.While
"Node"works (and matchespackages/meet-transcriptions), consider aligning with the root config orpackages/mcp-proxywhich uses"Bundler"for consistency across CLI packages.♻️ Optional: align with root config
- "moduleResolution": "Node", + "moduleResolution": "Bundler",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/await-until/tsconfig.json` at line 5, The package's tsconfig currently sets "moduleResolution": "Node"; change the tsconfig's moduleResolution value to match the root config ("bundler") for consistent ESM handling — update the "moduleResolution" property in the package tsconfig (referencing the moduleResolution key) to "bundler" so it aligns with the root and other CLI packages that use bundler/Bundler.
2-19: MissingdeclarationanddeclarationMapoptions.Other packages in this monorepo (e.g.,
mcp-proxy,aws-secrets-manager) includedeclaration: trueanddeclarationMap: trueto generate.d.tsfiles for TypeScript consumers. If this package is intended to be imported programmatically, consider adding these options.♻️ Optional: add declaration generation
"noFallthroughCasesInSwitch": true, "moduleDetection": "force", - "skipLibCheck": true + "skipLibCheck": true, + "declaration": true, + "declarationMap": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/await-until/tsconfig.json` around lines 2 - 19, Add TypeScript declaration generation to the compiler options by setting "declaration": true and "declarationMap": true inside the existing "compilerOptions" block (alongside "outDir" and "rootDir") so the build emits .d.ts files for consumers; ensure these keys are added at the same level as "target"/"module" and keep "outDir" intact so declarations land alongside compiled output.packages/await-until/src/server.ts (1)
51-59: Consider extracting pattern validation to a shared helper.The pattern-required validation is repeated across three tools (lines 51-59, 90-97, 114-121). A small helper would reduce duplication:
function validatePatternRequired(match: string, pattern?: string): string | null { const needsPattern = ["contains", "not_contains", "equals", "not_equals", "regex"]; if (needsPattern.includes(match) && !pattern) { return `Error: 'pattern' is required when match is '${match}'`; } return null; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/await-until/src/server.ts` around lines 51 - 59, Extract the repeated pattern-required check into a shared helper (e.g., validatePatternRequired(match: string, pattern?: string): string | null) and replace the three inline checks that examine validated.match and validated.pattern with calls to this helper; update the callers to return the helper's error string (wrapped into the existing content/isError response) when non-null. Ensure the helper contains the same match set ["contains","not_contains","equals","not_equals","regex"] and is imported/placed so functions handling the three tools can call it, preserving existing response shape where the error message is used.packages/await-until/src/pollers.ts (2)
229-234: Empty conditional in catch block is confusing.The
ifstatement checks match types but does nothing. If this is intentional (continue polling when file access fails for these match types), simplify by removing the conditional entirely.♻️ Proposed simplification
} catch (err) { lastOutput = err instanceof Error ? err.message : String(err); - if (params.match === "exists" || params.match === "contains" || params.match === "regex" || params.match === "not_empty") { - // noop - } + // File access failed; continue polling for exists/contains/regex/not_empty }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/await-until/src/pollers.ts` around lines 229 - 234, The catch block assigns lastOutput but contains an empty if checking params.match ("exists"/"contains"/"regex"/"not_empty"); remove that no-op conditional in the catch to simplify the logic in pollers.ts (leave the lastOutput assignment as-is) or, if the empty branch is intentional, replace it with a short clarifying comment referencing params.match to explain why we continue polling on file access errors; locate this logic around the catch that sets lastOutput and update accordingly.
278-298: Each poll iteration spawns a new MCP child process.
callMcpToolcreates a newStdioClientTransportand spawns a child process on every iteration. For long-running polls with short intervals, this could be resource-intensive.Consider reusing the client connection across iterations or documenting this behavior.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/await-until/src/pollers.ts` around lines 278 - 298, The loop currently calls callMcpTool each iteration which constructs a new StdioClientTransport and spawns a child process, causing heavy resource use for long polls; change pollers.ts to create and reuse a single client/transport outside the while loop (e.g., instantiate the StdioClientTransport or a persistent client once before the loop that callMcpTool can accept or the loop can call directly), use that client for each iteration, handle transient errors by attempting to reinitialize the client if it fails, and ensure the transport/client is properly closed or disposed after success, timeout, or unrecoverable error; reference callMcpTool and StdioClientTransport for where to move initialization, reuse, reinit-on-error, and cleanup logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/await-until/eslint.config.js`:
- Line 29: Remove the mistaken runtime global entry "Record" from the eslint
config's globals object (the "globals" map in eslint.config.js) because Record
is a TypeScript compile-time utility type, not a runtime value; simply delete
the Record: "readonly" line from the globals list (no replacement needed) so
ESLint no longer treats Record as a global identifier.
In `@packages/await-until/README.md`:
- Around line 246-254: The Development section in README.md lists a non-existent
`pnpm test` command which breaks CI; either remove the `pnpm test` line from the
command sequence in the Development section or add test files and a test script
(e.g., implement a test runner and package.json "test" script) so `pnpm test`
works; update the README's command block accordingly to reflect the chosen
approach (remove the `pnpm test` line if not adding tests, or keep it and add
the tests + package.json "test" script).
In `@packages/await-until/src/index.ts`:
- Around line 5-17: The module runs side-effectful startup code at import time
(creating AwaitUntilMCPServer, calling server.setupGracefulShutdown() and await
server.start()), which should be moved out of the library entrypoint; extract
that block into a new CLI entry file (e.g., cli.ts) that imports
AwaitUntilMCPServer and runs the try/catch startup logic, keep src/index.ts to
only export library symbols (AwaitUntilMCPServer export can remain if desired
but remove startup calls), and update package.json "bin" to point to the new CLI
so imports like pollCommand, pollUrl, pollFile, pollMcp, findMcpConfig,
getServerConfig, callMcpTool are safe to import without starting the server.
In `@packages/await-until/src/pollers.ts`:
- Line 52: The code currently hardcodes "/bin/bash" for the shell option (see
the "shell: shell ?? \"/bin/bash\"" expression); replace that with a
platform-aware default such as using process.env.SHELL when present and falling
back to process.platform === "win32" ? "cmd.exe" (or "powershell.exe" if you
prefer) : "/bin/bash", i.e. change the default expression to something like
shell ?? process.env.SHELL ?? (process.platform === "win32" ? "cmd.exe" :
"/bin/bash"); ensure this change is applied where the shell option is set
(pollers.ts) and add a short comment noting Windows vs POSIX defaults.
---
Nitpick comments:
In `@packages/await-until/eslint.config.js`:
- Line 45: The "no-undef" ESLint rule is conflicting with TypeScript; update the
ESLint config to remove or disable "no-undef" and instead rely on TypeScript's
checking (or replace it with the TypeScript-aware rule
"@typescript-eslint/no-undef" if that plugin is used). Locate the rule entry
"no-undef": "error" in the eslint.config.js and either delete it or set it to
"off", or enable "@typescript-eslint/no-undef" in rules and ensure
"@typescript-eslint" is in the plugins/extends so the TypeScript-aware rule is
used.
In `@packages/await-until/src/pollers.ts`:
- Around line 229-234: The catch block assigns lastOutput but contains an empty
if checking params.match ("exists"/"contains"/"regex"/"not_empty"); remove that
no-op conditional in the catch to simplify the logic in pollers.ts (leave the
lastOutput assignment as-is) or, if the empty branch is intentional, replace it
with a short clarifying comment referencing params.match to explain why we
continue polling on file access errors; locate this logic around the catch that
sets lastOutput and update accordingly.
- Around line 278-298: The loop currently calls callMcpTool each iteration which
constructs a new StdioClientTransport and spawns a child process, causing heavy
resource use for long polls; change pollers.ts to create and reuse a single
client/transport outside the while loop (e.g., instantiate the
StdioClientTransport or a persistent client once before the loop that
callMcpTool can accept or the loop can call directly), use that client for each
iteration, handle transient errors by attempting to reinitialize the client if
it fails, and ensure the transport/client is properly closed or disposed after
success, timeout, or unrecoverable error; reference callMcpTool and
StdioClientTransport for where to move initialization, reuse, reinit-on-error,
and cleanup logic.
In `@packages/await-until/src/server.ts`:
- Around line 51-59: Extract the repeated pattern-required check into a shared
helper (e.g., validatePatternRequired(match: string, pattern?: string): string |
null) and replace the three inline checks that examine validated.match and
validated.pattern with calls to this helper; update the callers to return the
helper's error string (wrapped into the existing content/isError response) when
non-null. Ensure the helper contains the same match set
["contains","not_contains","equals","not_equals","regex"] and is imported/placed
so functions handling the three tools can call it, preserving existing response
shape where the error message is used.
In `@packages/await-until/tsconfig.json`:
- Line 5: The package's tsconfig currently sets "moduleResolution": "Node";
change the tsconfig's moduleResolution value to match the root config
("bundler") for consistent ESM handling — update the "moduleResolution" property
in the package tsconfig (referencing the moduleResolution key) to "bundler" so
it aligns with the root and other CLI packages that use bundler/Bundler.
- Around line 2-19: Add TypeScript declaration generation to the compiler
options by setting "declaration": true and "declarationMap": true inside the
existing "compilerOptions" block (alongside "outDir" and "rootDir") so the build
emits .d.ts files for consumers; ensure these keys are added at the same level
as "target"/"module" and keep "outDir" intact so declarations land alongside
compiled output.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f3f8f927-0757-439b-965e-2eabc9ffbc6b
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (14)
README.mdpackages/await-until/README.mdpackages/await-until/eslint.config.jspackages/await-until/package.jsonpackages/await-until/src/index.tspackages/await-until/src/mcp-client.tspackages/await-until/src/pollers.tspackages/await-until/src/server.tspackages/await-until/src/types.tspackages/await-until/tsconfig.jsonpackages/mysql/README.mdpackages/npm-registry/README.mdpackages/postgresql/README.mdpackages/tempmail/README.md
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/await-until/package.json (1)
17-18: Consider adding unit tests before release.The
--passWithNoTestsflag indicates no tests exist yet. While manual testing was performed per the PR description, adding automated tests for the polling logic (especially edge cases like timeouts, regex matching, and error handling) would improve reliability and prevent regressions.This can be deferred if the package is intended for internal use initially.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/await-until/package.json` around lines 17 - 18, The package currently ships test scripts with --passWithNoTests but lacks unit tests; add automated Vitest specs for the polling logic (create tests in a __tests__ or tests folder) covering key functions such as the module's waitUntil/awaitUntil/pollUntil entrypoints: write cases for immediate success, timeout behavior, regex matching, thrown errors from the predicate, and cancellation/retry behavior; update package.json test/test:cov scripts to remove --passWithNoTests once tests exist and ensure tests import the polling functions directly to assert timing, returned values, and that errors/timeouts are raised as expected.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/await-until/package.json`:
- Around line 39-42: Update the `@modelcontextprotocol/sdk` dependency entry in
packages/await-until/package.json to a known published version (e.g., replace
"^1.0.0" with a tested release like "^1.26.0" or another specific compatible
version) so npm can resolve the package; edit the "dependencies" block where
"@modelcontextprotocol/sdk" is declared to point to the chosen valid version and
run an install to verify resolution.
---
Nitpick comments:
In `@packages/await-until/package.json`:
- Around line 17-18: The package currently ships test scripts with
--passWithNoTests but lacks unit tests; add automated Vitest specs for the
polling logic (create tests in a __tests__ or tests folder) covering key
functions such as the module's waitUntil/awaitUntil/pollUntil entrypoints: write
cases for immediate success, timeout behavior, regex matching, thrown errors
from the predicate, and cancellation/retry behavior; update package.json
test/test:cov scripts to remove --passWithNoTests once tests exist and ensure
tests import the polling functions directly to assert timing, returned values,
and that errors/timeouts are raised as expected.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: bed1cea1-af67-4d7c-942e-6d66250833d2
📒 Files selected for processing (1)
packages/await-until/package.json
- Remove Record from eslint globals (TS type, not runtime) - Split CLI from library exports (no side-effects on import) - Cross-platform shell default (win32 support) - Remove pnpm test from README dev section
SDK 1.23+ requires zod ^3.25 (Zod 4 mini) which causes type incompatibilities with ZodOptional/ZodDefault in registerTool. Pin to 1.22.0 which works with zod ^3.22.4.
Descricao
Novo MCP server
@arvoretech/await-until-mcpque fornece primitivas de polling/waiting. Quatro tools:await_until_command: executa um comando shell repetidamente até o output bater com uma condiçãoawait_until_url: faz polling de endpoints HTTP até retornar status/body esperadoawait_until_file: monitora o filesystem até um arquivo existir, sumir, ou conter conteúdoawait_until_mcp: chama tools de outros MCP servers configurados no mcp.json (auto-detecta Kiro, Cursor, Claude Desktop, com suporte a upstreams do mcp-proxy)Todos suportam
interval_seconds,timeout_seconds, e condições de match configuráveis (contains, regex, equals, etc).Etiquetas (Labels)
Historia Relacionada
N/A
Motivacao e Contexto
Agentes precisam esperar condições assíncronas (serviços subindo, deploys completando, dados aparecendo no banco, flags sendo habilitadas). Esse MCP server resolve isso com polling nativo, sem depender do agente manter o loop manualmente.
Como Isso Foi Testado?
await_until_command(echo),await_until_url(httpbin),await_until_file(package.json),await_until_mcp(npm-registry via mcp-proxy, agent-teams-lead direto, team-memory com env vars)Analise de Risco e Impacto
Capturas de Tela ou Auxilios Visuais (se apropriado)
N/A
Summary by CodeRabbit
New Features
Documentation
Chores