Skip to content

fix: prevent ambiguous multi-upstream resource routing - #44

Merged
mohanagy merged 6 commits into
developmentfrom
fix/issue-5-multi-upstream-capabilities
Jul 10, 2026
Merged

fix: prevent ambiguous multi-upstream resource routing#44
mohanagy merged 6 commits into
developmentfrom
fix/issue-5-multi-upstream-capabilities

Conversation

@mohanagy

@mohanagy mohanagy commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Summary

  • Omit resource and prompt capabilities/handlers for empty or multi-entry upstream bundles so MCP returns standard -32601 instead of selecting the first server.
  • Require explicit upstream selection for multi-upstream manager callers while preserving normal single and sole-entry proxying.
  • Document the temporary aggregation boundary and surface it through server instructions and miftah_health.

Verification

  • npm test
  • npm run typecheck
  • npm run lint
  • npm run build
  • npm run check:pack

Tracks #5

Summary by CodeRabbit

  • New Features
    • Resource and prompt proxying is enabled only when exactly one upstream is configured or explicitly selected.
    • miftah_health now reports resource/prompt proxy availability (including a reason).
    • Ambiguous upstream selection now returns a clear machine-readable error code.
  • Bug Fixes
    • Resource/prompt capabilities are no longer advertised or handled when no upstream or multiple upstreams are configured (requests are rejected).
    • Improved consistency of error handling and secret redaction for discovery and reads.
  • Documentation
    • Updated configuration and architecture docs to match the single-upstream proxying rules and availability reporting.
  • Tests
    • Expanded integration/unit coverage for capability gating, ambiguous selection, and redaction behavior.

mohanagy and others added 3 commits July 11, 2026 01:27
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

MCP resource and prompt proxying is available only for standard or sole-upstream configurations. Multi-upstream selection requires explicit names, unavailable capabilities are omitted and reported by miftah_health, and tests and documentation cover these behaviors.

Changes

Resource and prompt proxy behavior

Layer / File(s) Summary
Explicit upstream selection contract
src/upstream/multi-upstream-process-manager.ts, src/utils/errors.ts, tests/multi-upstream.test.ts
Multiple configured upstreams now require explicit selection and return UPSTREAM_SELECTION_AMBIGUOUS when omitted.
Conditional resource and prompt proxying
src/mcp/server/miftah-server.ts
Capabilities and handlers are registered only when proxying is available; health metadata and redacted proxy errors reflect this state.
Topology coverage and documentation
tests/multi-upstream.test.ts, tests/fixtures/fake-upstream.mjs, README.md, docs/architecture.md, docs/config.md
Tests, fixture controls, and documentation cover zero-, single-, and multi-upstream resource/prompt behavior and secret redaction.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MCPClient
  participant MiftahServer
  participant MultiUpstreamProcessManager
  participant UpstreamSession
  MCPClient->>MiftahServer: Request resource or prompt
  MiftahServer->>MultiUpstreamProcessManager: Select sole upstream by name
  MultiUpstreamProcessManager->>UpstreamSession: Read resource or get prompt
  UpstreamSession-->>MiftahServer: Return result or error
  MiftahServer-->>MCPClient: Return redacted result or method-not-found
Loading

Possibly related issues

  • Issue 5 — Prevents implicit first-upstream selection and adds capability gating for ambiguous multi-upstream configurations.

Poem

A bunny hops through upstream lanes,
No guessing names or tangled trains.
Sole streams carry prompts with care,
Busy bundles leave them bare.
Health lights tell the reason why.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: preventing ambiguous routing when multiple upstreams are configured.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-5-multi-upstream-capabilities

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

@mohanagy

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/mcp/server/miftah-server.ts`:
- Around line 152-172: Wrap the ListResourcesRequestSchema and
ListPromptsRequestSchema handlers in the same try/catch pattern as
ReadResourceRequestSchema, redacting caught error messages with redactSecrets
and this.upstreams.getSecretValues() before rethrowing with the original error
as cause. Add regression coverage verifying secrets in resources/list and
prompts/list failure messages are absent from client-facing errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1d161755-d98f-482a-9abf-a3b88aa7110b

📥 Commits

Reviewing files that changed from the base of the PR and between 39ed987 and 3808996.

📒 Files selected for processing (7)
  • README.md
  • docs/architecture.md
  • docs/config.md
  • src/mcp/server/miftah-server.ts
  • src/upstream/multi-upstream-process-manager.ts
  • src/utils/errors.ts
  • tests/multi-upstream.test.ts

Comment thread src/mcp/server/miftah-server.ts
mohanagy and others added 2 commits July 11, 2026 01:48
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohanagy

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
src/mcp/server/miftah-server.ts (1)

150-199: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract the repeated redact-on-error wrapper.

All four handlers (ListResources, ReadResource, ListPrompts, GetPrompt) now duplicate the identical try/catch/redact block, differing only in the upstream call made. This exact duplication is what previously let two of the four handlers silently skip redaction (per the earlier review comment on lines 152-186) — consolidating into a shared helper removes that class of bug going forward.

♻️ Proposed refactor
+  private async proxyResourceOrPrompt<T>(
+    upstreamName: string | undefined,
+    call: (session: Awaited<ReturnType<typeof this.upstreams.get>>) => Promise<T>
+  ): Promise<T> {
+    try {
+      const session = await this.upstreams.get(this.profiles.current().activeProfile, upstreamName);
+      return redactSecrets(await call(session), this.upstreams.getSecretValues());
+    } catch (error) {
+      throw new Error(
+        redactSecrets(error instanceof Error ? error.message : String(error), this.upstreams.getSecretValues()),
+        { cause: error }
+      );
+    }
+  }
+
   if (this.resourcePromptProxy.available) {
     const upstreamName = this.resourcePromptProxy.upstreamName;
-    this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
-      try {
-        const session = await this.upstreams.get(this.profiles.current().activeProfile, upstreamName);
-        return redactSecrets(await session.listResources(), this.upstreams.getSecretValues());
-      } catch (error) {
-        throw new Error(
-          redactSecrets(error instanceof Error ? error.message : String(error), this.upstreams.getSecretValues()),
-          { cause: error }
-        );
-      }
-    });
+    this.server.setRequestHandler(ListResourcesRequestSchema, () =>
+      this.proxyResourceOrPrompt(upstreamName, (session) => session.listResources())
+    );

-    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
-      try {
-        const session = await this.upstreams.get(this.profiles.current().activeProfile, upstreamName);
-        return redactSecrets(await session.readResource(request.params), this.upstreams.getSecretValues());
-      } catch (error) {
-        throw new Error(
-          redactSecrets(error instanceof Error ? error.message : String(error), this.upstreams.getSecretValues()),
-          { cause: error }
-        );
-      }
-    });
+    this.server.setRequestHandler(ReadResourceRequestSchema, (request) =>
+      this.proxyResourceOrPrompt(upstreamName, (session) => session.readResource(request.params))
+    );

     // ...same pattern for ListPrompts / GetPrompt
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mcp/server/miftah-server.ts` around lines 150 - 199, Extract the
duplicated upstream request error-handling into a shared helper near the handler
setup, accepting an operation callback and applying secret redaction to both
successful results and caught errors. Refactor ListResources, ReadResource,
ListPrompts, and GetPrompt to invoke this helper while retaining their
respective session methods and request parameters, ensuring all handlers
consistently use the centralized redaction logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/multi-upstream.test.ts`:
- Around line 225-269: Extend the test “redacts secrets from upstream resource
and prompt discovery failures” to also invoke client.readResource and
client.getPrompt with inputs that trigger upstream failures, then assert their
Error messages contain “[REDACTED]” and exclude the secret, matching the
existing listResources/listPrompts assertions. Use the relevant
ReadResourceRequestSchema and GetPromptRequestSchema handlers in
miftah-server.ts to ensure the new cases exercise their redact-on-error paths.

---

Outside diff comments:
In `@src/mcp/server/miftah-server.ts`:
- Around line 150-199: Extract the duplicated upstream request error-handling
into a shared helper near the handler setup, accepting an operation callback and
applying secret redaction to both successful results and caught errors. Refactor
ListResources, ReadResource, ListPrompts, and GetPrompt to invoke this helper
while retaining their respective session methods and request parameters,
ensuring all handlers consistently use the centralized redaction logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4200bb93-1958-443a-8b49-a915bc625606

📥 Commits

Reviewing files that changed from the base of the PR and between 3808996 and 205f1c3.

📒 Files selected for processing (3)
  • src/mcp/server/miftah-server.ts
  • tests/fixtures/fake-upstream.mjs
  • tests/multi-upstream.test.ts

Comment thread tests/multi-upstream.test.ts Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohanagy

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
src/mcp/server/miftah-server.ts (1)

171-234: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate redact-and-rethrow logic across three call sites.

The redactSecrets(error.message/String(error), this.upstreams.getSecretValues()) pattern is repeated in handleUpstreamTool's catch (Lines 224-228), handleManagement's catch (Lines 290-292), and proxyResourcePrompt's catch (Lines 332-336). Consider extracting a small private redactError(error: unknown): string helper to reduce duplication and keep the redaction behavior consistent if it needs to change later.

♻️ Proposed helper
+  private redactError(error: unknown): string {
+    return redactSecrets(
+      error instanceof Error ? error.message : String(error),
+      this.upstreams.getSecretValues()
+    );
+  }

Then replace the three inline blocks with this.redactError(error).

Also applies to: 236-294, 325-339

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mcp/server/miftah-server.ts` around lines 171 - 234, Extract a private
redactError(error: unknown): string helper on the server class that formats
Error messages or unknown values with String(error) and passes them through
redactSecrets using this.upstreams.getSecretValues(). Replace the duplicated
inline redaction logic in handleUpstreamTool, handleManagement, and
proxyResourcePrompt catch blocks with this.redactError(error), preserving each
caller’s existing error-code and response behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/mcp/server/miftah-server.ts`:
- Around line 171-234: Extract a private redactError(error: unknown): string
helper on the server class that formats Error messages or unknown values with
String(error) and passes them through redactSecrets using
this.upstreams.getSecretValues(). Replace the duplicated inline redaction logic
in handleUpstreamTool, handleManagement, and proxyResourcePrompt catch blocks
with this.redactError(error), preserving each caller’s existing error-code and
response behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b5a1eb00-3e71-4e8e-9205-7ddd64e97a67

📥 Commits

Reviewing files that changed from the base of the PR and between 205f1c3 and 2e274e9.

📒 Files selected for processing (3)
  • src/mcp/server/miftah-server.ts
  • tests/fixtures/fake-upstream.mjs
  • tests/multi-upstream.test.ts

@mohanagy
mohanagy merged commit de49bff into development Jul 10, 2026
2 checks passed
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.

1 participant