Skip to content

Conversation

@gulivan
Copy link
Collaborator

@gulivan gulivan commented Oct 17, 2025

Summary by CodeRabbit

  • New Features

    • Added configurable concurrency limits for resource and prompt fetches during server verification. This allows tuning performance based on server capacity (default: 5 concurrent operations).
  • Chores

    • Updated internal dependencies and constants to support concurrency management features.

gulivan and others added 3 commits October 17, 2025 23:12
- Extract MCP_TOOL_TOKEN_MULTIPLIER constant to tokens.ts for better maintainability
- Add detailed documentation explaining token multiplier methodology
- Create createMockClient helper in tests to reduce duplication
- Replace string literal 'stdio' with MCPServerType.STDIO enum for type safety
- Add concurrency limiting (5 concurrent) for resource/prompt fetches to prevent server overload
- Add p-limit dependency for concurrency control

These changes improve code quality, reduce magic numbers, and prevent overwhelming slow MCP servers with parallel requests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Add maxConcurrentFetches option to MCPVerificationOptions to allow users to configure the concurrency limit when fetching resources and prompts from MCP servers.

Changes:
- Add maxConcurrentFetches parameter to MCPVerificationOptions (default: 5)
- Update verifyServer JSDoc with example usage
- Apply configurable limit to both resource and prompt fetching
- Add 3 tests to verify default and custom concurrency limits

This allows external applications to tune concurrency based on their server characteristics - use higher limits for fast servers or lower limits for slow/rate-limited servers.

Example usage:
```typescript
const result = await verifier.verifyServer(server, {
  maxConcurrentFetches: 10  // Use 10 concurrent fetches for fast servers
});
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Update library API README with concise documentation for the new maxConcurrentFetches option:
- Added to options parameter list
- Added example showing fast vs slow server tuning
- Updated type definitions
- Added to performance considerations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@coderabbitai
Copy link

coderabbitai bot commented Oct 17, 2025

Walkthrough

This pull request introduces concurrency control for MCP client resource and prompt fetching operations by adding a new p-limit dependency, defining a MAX_CONCURRENT_FETCHES constant, and extending MCPVerificationOptions with an optional maxConcurrentFetches parameter to limit parallel fetch operations.

Changes

Cohort / File(s) Summary
Dependency addition
package.json
Added p-limit ^7.1.1 for concurrency limiting.
Constants definition
src/constants/mcp.ts, src/constants/tokens.ts
Added MAX_CONCURRENT_FETCHES = 5 and MCP_TOOL_TOKEN_MULTIPLIER = 9 constants with documentation.
Constants re-exports
src/constants/index.ts
Extended public re-exports to include MAX_CONCURRENT_FETCHES and MCP_TOOL_TOKEN_MULTIPLIER.
Type extension
src/types/index.ts
Added optional maxConcurrentFetches?: number field to MCPVerificationOptions interface.
Core implementation
src/core/mcpClient.ts
Introduced p-limit-based concurrency control for resource and prompt fetching, replaced hardcoded token multiplier with constant, added maxConcurrentFetches option handling with safe cleanup/abort checks, and updated runtime type checks to use MCPServerType enum.
Documentation
src/lib/verifier/README.md
Added documentation for maxConcurrentFetches option with tuning guidance and defaults.
Tests
tests/core/mcpClient.test.ts
Added MockClient factory (createMockClient), refactored existing tests to use factory, and introduced new concurrency verification test suite.

Sequence Diagram

sequenceDiagram
    participant Client as MCP Client
    participant Limiter as p-limit<br/>(concurrency)
    participant Tasks as Resource/Prompt<br/>Fetch Tasks
    
    Client->>Client: verifyServer({maxConcurrentFetches: N})
    Client->>Limiter: Create limit(N)
    
    loop For each resource/prompt
        Client->>Limiter: limit(() => fetchTask())
        par Parallel (bounded)
            Limiter->>Tasks: Execute up to N tasks
            Tasks-->>Limiter: Complete
        end
    end
    
    Limiter-->>Client: All fetches completed
    Client->>Client: Return verification result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

The changes involve focused concurrency control logic modifications in mcpClient.ts, new constant definitions, type extensions, and test additions. While the implementation is relatively straightforward, reviewers should verify the correctness of p-limit integration, default concurrency values, and abort/cleanup handling in async operations across resource and prompt fetching.

Possibly related PRs

  • feat(verifier): add advanced MCP verification options #6: Directly related—both modify mcpClient.ts and verifier functionality, introduce configurable verification options for resource/prompt fetching, add/replace token multiplier constants (value 9), and extend exports with the same constants (MAX_CONCURRENT_FETCHES, MCP_TOOL_TOKEN_MULTIPLIER).

Poem

🐰✨ With p-limit's gentle rein so tight,
Five fetches hop in parallel delight,
No queue storms now, just bounded grace,
Each resource finds its bounded place! 🚀

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Feat/enhanced mcp verification" is directly related to the main changes in this pull request. The changeset centers on enhancing the MCP verification system by introducing concurrency control capabilities through the new maxConcurrentFetches option, adding the p-limit dependency, and defining new constants (MAX_CONCURRENT_FETCHES and MCP_TOOL_TOKEN_MULTIPLIER). The title accurately reflects this primary objective of improving MCP verification functionality. While the title could be more specific about the type of enhancement (e.g., mentioning concurrency control), it is sufficiently clear and directly captures the essence of the core change without being misleading or overly vague.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/enhanced-mcp-verification

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
package.json (1)

55-56: Engines mismatch: p-limit@7 requires Node 20+, but engines allow Node 18

This will break users on Node 18 at install/runtime. Choose one:

Option A — raise Node baseline:

   "engines": {
-    "node": ">=18.0.0"
+    "node": ">=20.0.0"
   },

Option B — keep Node 18 support: downgrade p-limit to v6.x (no code changes needed here):

-    "p-limit": "^7.1.1",
+    "p-limit": "^6.2.0",

Also applies to: 72-73

src/core/mcpClient.ts (1)

329-356: Prevent unhandled rejection when timeout wins the race

If timeoutPromise rejects first, connectAndVerify may later reject with no awaiting consumer, causing an unhandled rejection. Attach a catch to connectPromise before racing.

-      // Connect to the server with timeout
-      const connectPromise = this.connectAndVerify(client, transport, server, abortController.signal, options);
-
-      const capabilities = await Promise.race([connectPromise, timeoutPromise]);
+      // Connect to the server with timeout
+      const connectPromise = this.connectAndVerify(
+        client,
+        transport,
+        server,
+        abortController.signal,
+        options
+      );
+      // Swallow rejection if timeout wins the race to avoid unhandled rejection
+      connectPromise.catch(() => {});
+      const capabilities = await Promise.race([connectPromise, timeoutPromise]);
🧹 Nitpick comments (3)
src/lib/verifier/README.md (1)

268-285: Document Node.js 20+ requirement and align default timeout

Since concurrency uses p-limit v7, call out “Requires Node.js ≥20” in this section. Also, earlier the constructor docs state a 10s default but code uses 30s (DEFAULT_CONNECTION_TIMEOUT_MS = 30000). Please sync the README.

src/types/index.ts (1)

222-223: Constrain and validate maxConcurrentFetches

Type allows any number. At runtime, ensure it’s a positive safe integer with a sane upper bound (e.g., 1–50). See mcpClient comment for a concrete guard.

src/core/mcpClient.ts (1)

696-703: Avoid double-closing the client (cleanup already handled by caller)

client.close() is called here and again in verifyServer’s finally. While errors are ignored, the double close is redundant. Prefer centralizing cleanup in verifyServer.

-    } finally {
-      // Clean up the connection
-      try {
-        await client.close();
-      } catch (error) {
-        // Ignore cleanup errors
-      }
-    }
+    } finally {
+      // Cleanup is handled by the caller (verifyServer.finally)
+    }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 030664f and 89f66d0.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • package.json (1 hunks)
  • src/constants/index.ts (1 hunks)
  • src/constants/mcp.ts (1 hunks)
  • src/constants/tokens.ts (1 hunks)
  • src/core/mcpClient.ts (9 hunks)
  • src/lib/verifier/README.md (4 hunks)
  • src/types/index.ts (1 hunks)
  • tests/core/mcpClient.test.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write clean, maintainable code following TypeScript best practices
Use TypeScript for type safety when applicable
Follow consistent naming conventions
Add comments for complex logic
Maintain consistency with existing code style
Add appropriate error handling
Never expose sensitive information in code or logs
Follow security best practices for TypeScript
Validate all inputs
Use environment variables for configuration
Document all public APIs, functions, and classes with clear descriptions

Files:

  • src/constants/mcp.ts
  • src/constants/tokens.ts
  • tests/core/mcpClient.test.ts
  • src/core/mcpClient.ts
  • src/constants/index.ts
  • src/types/index.ts
package.json

📄 CodeRabbit inference engine (CLAUDE.md)

Use npm for dependency management

Files:

  • package.json
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{test,spec}.ts: Write unit tests for all business logic using Jest
Test edge cases and error scenarios
Update tests when modifying functionality

Files:

  • tests/core/mcpClient.test.ts
🧬 Code graph analysis (3)
src/constants/mcp.ts (1)
src/constants/index.ts (1)
  • MAX_CONCURRENT_FETCHES (1-1)
tests/core/mcpClient.test.ts (2)
src/types/index.ts (1)
  • MCPServerConfig (80-88)
src/lib/types/index.ts (1)
  • MCPServerType (31-31)
src/core/mcpClient.ts (3)
src/constants/tokens.ts (1)
  • MCP_TOOL_TOKEN_MULTIPLIER (20-20)
src/constants/mcp.ts (1)
  • MAX_CONCURRENT_FETCHES (11-11)
src/lib/types/index.ts (1)
  • MCPServerType (31-31)
🔇 Additional comments (7)
src/constants/mcp.ts (1)

10-12: LGTM: default concurrency cap added

Sensible default and clear doc string. No issues.

src/lib/verifier/README.md (1)

75-76: Option field docs look good

The new option and tuning guidance are clear and match the implementation/default (5).

Also applies to: 475-476

src/constants/tokens.ts (1)

6-20: LGTM: multiplier constant and rationale

Clear documentation and scoped export. Matches usage in token counting.

src/constants/index.ts (1)

1-2: Re-exports updated correctly

Surface now includes MAX_CONCURRENT_FETCHES and MCP_TOOL_TOKEN_MULTIPLIER. Good.

src/core/mcpClient.ts (1)

818-835: Good: sanitize connection details in error output

Avoids leaking credentials and query params; STDIO command logging is helpful for debugging.

tests/core/mcpClient.test.ts (2)

25-49: LGTM! Excellent refactor to reduce test duplication.

The MockClient type and createMockClient helper function provide a clean, reusable pattern for test setup. The default implementations are sensible, and the override mechanism using the spread operator allows for flexible customization.


480-690: LGTM! Clean migration to the new mock helper.

The tests now consistently use createMockClient, which improves readability and maintainability by centralizing mock setup logic.

Comment on lines +573 to +576
// Fetch resources with concurrency limiting to avoid overwhelming servers
const concurrencyLimit = options?.maxConcurrentFetches || MAX_CONCURRENT_FETCHES;
const limit = pLimit(concurrencyLimit);
const resourcePromises = resourcesResponse.resources.map((resource) => limit(async () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate maxConcurrentFetches to avoid p-limit throwing or runaway parallelism

Current logic treats 0 as default (ok) but allows negatives/NaN/huge values. Guard to 1–50 safe integer.

-        const concurrencyLimit = options?.maxConcurrentFetches || MAX_CONCURRENT_FETCHES;
-        const limit = pLimit(concurrencyLimit);
+        const raw = options?.maxConcurrentFetches;
+        const concurrencyLimit =
+          Number.isSafeInteger(raw) && (raw as number) > 0
+            ? Math.min(raw as number, 50)
+            : MAX_CONCURRENT_FETCHES;
+        const limit = pLimit(concurrencyLimit);

Apply the same guard in the prompts section.

-        const concurrencyLimit = options?.maxConcurrentFetches || MAX_CONCURRENT_FETCHES;
-        const limit = pLimit(concurrencyLimit);
+        const raw = options?.maxConcurrentFetches;
+        const concurrencyLimit =
+          Number.isSafeInteger(raw) && (raw as number) > 0
+            ? Math.min(raw as number, 50)
+            : MAX_CONCURRENT_FETCHES;
+        const limit = pLimit(concurrencyLimit);

Also applies to: 630-633

🤖 Prompt for AI Agents
In src/core/mcpClient.ts around lines 573-576 (and also apply to 630-633), the
code reads options?.maxConcurrentFetches directly which can be negative, NaN,
zero or unbounded; p-limit can throw or you can end up with runaway concurrency.
Validate options.maxConcurrentFetches as a safe integer clamped to the range
1–50: if it's not a finite safe integer in [1,50] fall back to
MAX_CONCURRENT_FETCHES; then use that validated value when creating pLimit.
Apply the same validation logic in the prompts section (lines ~630-633) so both
usages share the same guarded, clamped concurrency value.

Comment on lines +1273 to +1336
it('should use default MAX_CONCURRENT_FETCHES when option not specified', async () => {
const mockClient = createMockClient({
listResources: vi.fn().mockResolvedValue({
resources: [
{ uri: 'file:///test1.txt' },
{ uri: 'file:///test2.txt' },
{ uri: 'file:///test3.txt' }
]
})
});

const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
vi.mocked(Client).mockImplementation(() => mockClient as any);

const result = await verifier.verifyServer(testServer, { timeout: 5000 });

expect(result.status).toBe('success');
// Verify resources were fetched (concurrency limit was applied)
expect(result.capabilities?.resources).toHaveLength(3);
});

it('should use custom maxConcurrentFetches when provided', async () => {
const mockClient = createMockClient({
listResources: vi.fn().mockResolvedValue({
resources: Array.from({ length: 20 }, (_, i) => ({ uri: `file:///test${i}.txt` }))
})
});

const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
vi.mocked(Client).mockImplementation(() => mockClient as any);

const result = await verifier.verifyServer(testServer, {
timeout: 5000,
maxConcurrentFetches: 2 // Very low limit
});

expect(result.status).toBe('success');
// Verify all resources were fetched despite low concurrency
expect(result.capabilities?.resources).toHaveLength(20);
});

it('should apply maxConcurrentFetches to prompt fetching as well', async () => {
const mockClient = createMockClient({
listPrompts: vi.fn().mockResolvedValue({
prompts: Array.from({ length: 15 }, (_, i) => ({
name: `prompt-${i}`,
description: `Test prompt ${i}`
}))
})
});

const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
vi.mocked(Client).mockImplementation(() => mockClient as any);

const result = await verifier.verifyServer(testServer, {
timeout: 5000,
maxConcurrentFetches: 3
});

expect(result.status).toBe('success');
// Verify all prompts were fetched
expect(result.capabilities?.prompts).toHaveLength(15);
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Concurrency tests don't trigger the code paths they claim to test.

These tests have fundamental flaws:

  1. Test 1 (lines 1273-1292): Doesn't set includeResourceContents: true, so readResource is never called. The test only verifies listResources results, which aren't subject to the concurrency limiting.

  2. Test 2 (lines 1294-1312): Same issue - without includeResourceContents: true, no resource content fetching occurs, so concurrency limiting isn't exercised.

  3. Test 3 (lines 1314-1336): Doesn't set includePromptDetails: true, so getPrompt is never called. Only verifies listPrompts results.

Additionally, even if the correct options were set, these tests only verify that all items are eventually fetched—they don't actually verify that the concurrency limit is respected. The tests would pass even if the concurrency limiting code was completely removed.

To fix these tests:

Apply this diff to properly trigger concurrency-limited operations:

     it('should use default MAX_CONCURRENT_FETCHES when option not specified', async () => {
       const mockClient = createMockClient({
         listResources: vi.fn().mockResolvedValue({
           resources: [
             { uri: 'file:///test1.txt' },
             { uri: 'file:///test2.txt' },
             { uri: 'file:///test3.txt' }
           ]
-        })
+        }),
+        readResource: vi.fn().mockResolvedValue({
+          contents: [{ text: 'test content' }]
+        })
       });

       const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
       vi.mocked(Client).mockImplementation(() => mockClient as any);

-      const result = await verifier.verifyServer(testServer, { timeout: 5000 });
+      const result = await verifier.verifyServer(testServer, {
+        timeout: 5000,
+        includeResourceContents: true
+      });

       expect(result.status).toBe('success');
-      // Verify resources were fetched (concurrency limit was applied)
       expect(result.capabilities?.resources).toHaveLength(3);
+      expect(mockClient.readResource).toHaveBeenCalledTimes(3);
     });

     it('should use custom maxConcurrentFetches when provided', async () => {
       const mockClient = createMockClient({
         listResources: vi.fn().mockResolvedValue({
           resources: Array.from({ length: 20 }, (_, i) => ({ uri: `file:///test${i}.txt` }))
-        })
+        }),
+        readResource: vi.fn().mockResolvedValue({
+          contents: [{ text: 'test content' }]
+        })
       });

       const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
       vi.mocked(Client).mockImplementation(() => mockClient as any);

       const result = await verifier.verifyServer(testServer, {
         timeout: 5000,
-        maxConcurrentFetches: 2  // Very low limit
+        maxConcurrentFetches: 2,  // Very low limit
+        includeResourceContents: true
       });

       expect(result.status).toBe('success');
-      // Verify all resources were fetched despite low concurrency
       expect(result.capabilities?.resources).toHaveLength(20);
+      expect(mockClient.readResource).toHaveBeenCalledTimes(20);
     });

     it('should apply maxConcurrentFetches to prompt fetching as well', async () => {
       const mockClient = createMockClient({
         listPrompts: vi.fn().mockResolvedValue({
           prompts: Array.from({ length: 15 }, (_, i) => ({
             name: `prompt-${i}`,
             description: `Test prompt ${i}`
           }))
+        }),
+        getPrompt: vi.fn().mockResolvedValue({
+          messages: [{ role: 'user', content: 'test prompt' }]
         })
       });

       const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
       vi.mocked(Client).mockImplementation(() => mockClient as any);

       const result = await verifier.verifyServer(testServer, {
         timeout: 5000,
-        maxConcurrentFetches: 3
+        maxConcurrentFetches: 3,
+        includePromptDetails: true
       });

       expect(result.status).toBe('success');
-      // Verify all prompts were fetched
       expect(result.capabilities?.prompts).toHaveLength(15);
+      expect(mockClient.getPrompt).toHaveBeenCalledTimes(15);
     });

Note: These fixes ensure the concurrency code paths are executed. However, to truly verify concurrency limiting behavior (that at most N operations run simultaneously), you would need additional instrumentation, such as tracking in-flight operation counts or adding artificial delays to verify batching behavior.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should use default MAX_CONCURRENT_FETCHES when option not specified', async () => {
const mockClient = createMockClient({
listResources: vi.fn().mockResolvedValue({
resources: [
{ uri: 'file:///test1.txt' },
{ uri: 'file:///test2.txt' },
{ uri: 'file:///test3.txt' }
]
})
});
const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
vi.mocked(Client).mockImplementation(() => mockClient as any);
const result = await verifier.verifyServer(testServer, { timeout: 5000 });
expect(result.status).toBe('success');
// Verify resources were fetched (concurrency limit was applied)
expect(result.capabilities?.resources).toHaveLength(3);
});
it('should use custom maxConcurrentFetches when provided', async () => {
const mockClient = createMockClient({
listResources: vi.fn().mockResolvedValue({
resources: Array.from({ length: 20 }, (_, i) => ({ uri: `file:///test${i}.txt` }))
})
});
const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
vi.mocked(Client).mockImplementation(() => mockClient as any);
const result = await verifier.verifyServer(testServer, {
timeout: 5000,
maxConcurrentFetches: 2 // Very low limit
});
expect(result.status).toBe('success');
// Verify all resources were fetched despite low concurrency
expect(result.capabilities?.resources).toHaveLength(20);
});
it('should apply maxConcurrentFetches to prompt fetching as well', async () => {
const mockClient = createMockClient({
listPrompts: vi.fn().mockResolvedValue({
prompts: Array.from({ length: 15 }, (_, i) => ({
name: `prompt-${i}`,
description: `Test prompt ${i}`
}))
})
});
const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
vi.mocked(Client).mockImplementation(() => mockClient as any);
const result = await verifier.verifyServer(testServer, {
timeout: 5000,
maxConcurrentFetches: 3
});
expect(result.status).toBe('success');
// Verify all prompts were fetched
expect(result.capabilities?.prompts).toHaveLength(15);
});
});
it('should use default MAX_CONCURRENT_FETCHES when option not specified', async () => {
const mockClient = createMockClient({
listResources: vi.fn().mockResolvedValue({
resources: [
{ uri: 'file:///test1.txt' },
{ uri: 'file:///test2.txt' },
{ uri: 'file:///test3.txt' }
]
}),
readResource: vi.fn().mockResolvedValue({
contents: [{ text: 'test content' }]
})
});
const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
vi.mocked(Client).mockImplementation(() => mockClient as any);
const result = await verifier.verifyServer(testServer, {
timeout: 5000,
includeResourceContents: true
});
expect(result.status).toBe('success');
expect(result.capabilities?.resources).toHaveLength(3);
expect(mockClient.readResource).toHaveBeenCalledTimes(3);
});
it('should use custom maxConcurrentFetches when provided', async () => {
const mockClient = createMockClient({
listResources: vi.fn().mockResolvedValue({
resources: Array.from({ length: 20 }, (_, i) => ({ uri: `file:///test${i}.txt` }))
}),
readResource: vi.fn().mockResolvedValue({
contents: [{ text: 'test content' }]
})
});
const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
vi.mocked(Client).mockImplementation(() => mockClient as any);
const result = await verifier.verifyServer(testServer, {
timeout: 5000,
maxConcurrentFetches: 2,
includeResourceContents: true
});
expect(result.status).toBe('success');
expect(result.capabilities?.resources).toHaveLength(20);
expect(mockClient.readResource).toHaveBeenCalledTimes(20);
});
it('should apply maxConcurrentFetches to prompt fetching as well', async () => {
const mockClient = createMockClient({
listPrompts: vi.fn().mockResolvedValue({
prompts: Array.from({ length: 15 }, (_, i) => ({
name: `prompt-${i}`,
description: `Test prompt ${i}`
}))
}),
getPrompt: vi.fn().mockResolvedValue({
messages: [{ role: 'user', content: 'test prompt' }]
})
});
const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
vi.mocked(Client).mockImplementation(() => mockClient as any);
const result = await verifier.verifyServer(testServer, {
timeout: 5000,
maxConcurrentFetches: 3,
includePromptDetails: true
});
expect(result.status).toBe('success');
expect(result.capabilities?.prompts).toHaveLength(15);
expect(mockClient.getPrompt).toHaveBeenCalledTimes(15);
});
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants