Skip to content

feat: add deterministic MCP tool registry - #47

Merged
mohanagy merged 4 commits into
developmentfrom
feat/issue-8-capability-registry
Jul 11, 2026
Merged

feat: add deterministic MCP tool registry#47
mohanagy merged 4 commits into
developmentfrom
feat/issue-8-capability-registry

Conversation

@mohanagy

@mohanagy mohanagy commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

  • add immutable, per-profile tool snapshots with deterministic discovery, cold-call routing, schema guards, and stale-update protection
  • notify MCP clients to re-list after profile changes and active-profile restarts
  • restart every upstream in multi-upstream profile bundles without weakening fail-closed singular selection

Validation

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

Implements #8. This PR targets development; the issue will be closed manually after reviewed merge.

Summary by CodeRabbit

  • New Features

    • Tool discovery and routing are now profile-aware and consistent across concurrent requests.
    • Servers advertise tools.listChanged and send tools/list_changed after profile changes and active-profile restarts.
    • Upstream tools are exposed with deterministic namespacing (for example, <upstream>__<tool>).
  • Bug Fixes

    • Unknown or unavailable tools are rejected instead of forwarded.
    • Tool schema mismatches now return explicit errors; collisions fail discovery cleanly.
  • Documentation

    • Updated profile-switching and tool-discovery/notification semantics guidance.
  • Tests

    • Expanded coverage for notifications, refresh/retry behavior, restart invalidation, and error cases.

mohanagy and others added 2 commits July 11, 2026 04:02
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 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: acf46722-ce6c-4c8e-bd0d-a3eefa66ccf2

📥 Commits

Reviewing files that changed from the base of the PR and between 700f1fc and b207152.

📒 Files selected for processing (5)
  • src/upstream/multi-upstream-process-manager.ts
  • tests/fixtures/fake-upstream.mjs
  • tests/helpers/notifications.ts
  • tests/mcp-wrapper.test.ts
  • tests/multi-upstream.test.ts

📝 Walkthrough

Walkthrough

Miftah now manages upstream tools through immutable, cached per-profile snapshots. Profile revisions coordinate discovery and routing, stable errors cover unknown or incompatible tools, and profile changes or restarts notify clients to refresh tool listings.

Changes

Profile-aware tool registry and routing

Layer / File(s) Summary
Immutable tool snapshots and contracts
src/mcp/server/tool-registry.ts, src/utils/errors.ts
Tool discovery is cached per profile with single-flight generation handling, deterministic ordering, collision detection, route resolution, and stable fingerprints.
Profile revisions and restart coordination
src/profiles/profile-manager.ts, src/routing/routing-engine.ts, src/upstream/multi-upstream-process-manager.ts
Profile transitions increment revisions, active-profile fallback can use a captured profile, and all upstream managers can restart a profile concurrently.
MCP discovery, routing, and notifications
src/mcp/server/miftah-server.ts
MCP listing and calls use registry snapshots, management dispatch uses exact names, routed calls validate schemas, and relevant profile operations emit list-change notifications.
Behavioral validation and documented contracts
tests/fixtures/fake-upstream.mjs, tests/helpers/notifications.ts, tests/mcp-wrapper.test.ts, tests/multi-upstream.test.ts, tests/profile-manager.test.ts, README.md, docs/architecture.md, docs/config.md
Fixtures and tests cover discovery concurrency, schema changes, collisions, restarts, notifications, and namespaced tools; documentation records the updated behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant MiftahServer
  participant ToolRegistry
  participant UpstreamProcessManager
  Client->>MiftahServer: listTools or callTool
  MiftahServer->>ToolRegistry: get active-profile snapshot
  ToolRegistry->>UpstreamProcessManager: discover upstream tools
  UpstreamProcessManager-->>ToolRegistry: return tools and schemas
  ToolRegistry-->>MiftahServer: return snapshot and route
  MiftahServer-->>Client: tools or routed call result
  Client->>MiftahServer: change or restart profile
  MiftahServer-->>Client: tools/list_changed notification
Loading

Possibly related issues

  • mohanagy/miftah#8 — Covers deterministic tool registration, exact routing, schema checks, concurrency safety, and tool-list notifications implemented by this change.

Poem

A rabbit hops through tools in rows,
With cached-up maps and schemas aglow.
Profiles turn, snapshots sing,
Unknown calls get no forwarding wing.
“Refresh your list!” the notices say—
Then carrots route the proper way.


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Docstring Coverage ❌ Error Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: introducing a deterministic MCP tool registry.
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 feat/issue-8-capability-registry

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

@mohanagy

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 11, 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: 5

🤖 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/upstream/multi-upstream-process-manager.ts`:
- Around line 35-37: Update restartProfile to ensure
toolRegistry.invalidate(profile) executes regardless of whether any
manager.restart(profile) call fails: wrap the Promise.all restart attempts in a
try/finally block and perform invalidation in finally, preserving rejection of
restart errors.

In `@tests/mcp-wrapper.test.ts`:
- Around line 303-327: Strengthen the test “rejects unknown tool calls without
forwarding them upstream” by adding a fixture-visible call marker or invocation
counter, capturing its initial value before the unknown tool request, and
asserting it remains unchanged afterward. Keep the existing TOOL_NOT_FOUND
assertion, and ensure the marker is read from the upstream fixture through its
established mechanism.
- Around line 115-125: Make the notification-count assertions in the test around
client.callTool and the corresponding second occurrence verify stability, not
merely that the count reaches one: after waiting for the first notification, add
a bounded settling period or use an equivalent polling/helper assertion
confirming notifications remains exactly 1, while preserving the existing
timeout behavior.
- Line 411: Move the /TOOL_COLLISION/ regular expression used by the
client.listTools() rejection assertion to module scope as a reusable constant,
then reference that constant in the expect(...).rejects.toThrow() call to
satisfy prefer-static-regex.

In `@tests/multi-upstream.test.ts`:
- Around line 384-409: Replace the polling assertion in the test around
client.listTools() and the notifications counter with a stabilization wait that
allows notification delivery to settle, then assert notifications is exactly 1.
Preserve the existing notification handler and ensure the final assertion can
detect any later duplicate notification.
🪄 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: e3d3ac91-0601-4bf6-9082-0a5c1393796e

📥 Commits

Reviewing files that changed from the base of the PR and between d98a7d3 and 0b0afaa.

📒 Files selected for processing (13)
  • README.md
  • docs/architecture.md
  • docs/config.md
  • src/mcp/server/miftah-server.ts
  • src/mcp/server/tool-registry.ts
  • src/profiles/profile-manager.ts
  • src/routing/routing-engine.ts
  • src/upstream/multi-upstream-process-manager.ts
  • src/utils/errors.ts
  • tests/fixtures/fake-upstream.mjs
  • tests/mcp-wrapper.test.ts
  • tests/multi-upstream.test.ts
  • tests/profile-manager.test.ts

Comment thread src/upstream/multi-upstream-process-manager.ts
Comment thread tests/mcp-wrapper.test.ts Outdated
Comment thread tests/mcp-wrapper.test.ts
Comment thread tests/mcp-wrapper.test.ts Outdated
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 11, 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: 2

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)

162-189: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fingerprint-based cross-profile schema guard looks correct.

Resolving name against both the source and routed profile snapshots and comparing fingerprint before dispatch correctly prevents a cold call from silently using a mismatched schema after routing. This matches tools/mcp-wrapper.test.ts's TOOL_SCHEMA_MISMATCH test coverage.

One readability nit: const upstreamName = target.originalName; actually holds the raw tool name, not an upstream identifier — the real upstream selector is target.upstreamName (assigned to targetUpstream). Correctness is unaffected, but the naming makes this section harder to follow and easy to typo-swap in future edits.

♻️ Suggested rename for clarity
-      const upstreamName = target.originalName;
+      const originalToolName = target.originalName;
       const targetUpstream = target.upstreamName;
       const profile = this.profiles.get(route.profile);
-      const decision = this.policy.evaluate(profile.policy, upstreamName);
+      const decision = this.policy.evaluate(profile.policy, originalToolName);

(and update the remaining upstreamName references in this block accordingly)

🤖 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 162 - 189, Rename the local
variable `upstreamName` in `handleUpstreamTool` to reflect that it stores
`target.originalName`, such as `toolName`, and update every reference within the
block; keep `targetUpstream` reserved for `target.upstreamName`.
🤖 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 301-316: Update the miftah_restart_profile handler to await all
upstream restart operations settling before calling toolRegistry.invalidate or
notifyToolListChanged; avoid relying on Promise.all’s early rejection by using
Promise.allSettled or equivalent coordination in
MultiUpstreamProcessManager.restartProfile, while preserving error propagation
after every restart has completed.

In `@tests/mcp-wrapper.test.ts`:
- Around line 17-24: Extract notificationSettleMs and
expectExactlyOneToolListChanged from tests/mcp-wrapper.test.ts into a shared
tests/helpers/notifications.ts module, preserving their behavior and required
imports. Remove the duplicate definitions from mcp-wrapper.test.ts and
multi-upstream.test.ts, then import the shared symbols in both suites.

---

Outside diff comments:
In `@src/mcp/server/miftah-server.ts`:
- Around line 162-189: Rename the local variable `upstreamName` in
`handleUpstreamTool` to reflect that it stores `target.originalName`, such as
`toolName`, and update every reference within the block; keep `targetUpstream`
reserved for `target.upstreamName`.
🪄 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: 6d2c2c1b-d1ca-46ee-88f8-d9a539ac1fcf

📥 Commits

Reviewing files that changed from the base of the PR and between 0b0afaa and 700f1fc.

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

Comment thread src/mcp/server/miftah-server.ts
Comment thread tests/mcp-wrapper.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 11, 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.

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