Skip to content

Fallback MCP registry search - #190

Merged
getappz merged 2 commits into
masterfrom
task/60
Jul 15, 2026
Merged

getappz merged 2 commits into
masterfrom
task/60

Conversation

@getappz

@getappz getappz commented Jul 15, 2026 •

Copy link
Copy Markdown
Owner

Auto-opened on item done for 019f6293-b426-7140-8e21-e38115f0ee11.

Summary by CodeRabbit

  • New Features
    • Search now falls back to the official registry when local results are insufficient.
    • Tool and skill results include installation guidance and optional remote URLs.
    • Search results identify whether matches come from the local index or the official registry.
    • Registry results are limited and ranked after local matches.
  • Bug Fixes
    • Improved search coverage when matching tools or skills are not available locally.

- Add ureq HTTP client for registry.modelcontextprotocol.io
- search_with_fallback: local BM25 first, registry if under limit
- SkillHit gains install_hint/remote_url for registry hits
- Wire into skill tool handler
@coderabbitai

coderabbitai Bot commented Jul 15, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds official MCP Registry HTTP fallback search, registry provenance and installation metadata, quota-limited merging for gateway and skill results, and MCP server wiring for both skill and tool search actions.

Changes

Registry fallback search

Layer / File(s) Summary
Gateway registry HTTP lookup and hit contract
crates/gateway-registry/Cargo.toml, crates/gateway-registry/src/registry_search.rs, crates/gateway-registry/src/lib.rs
Adds the HTTP client dependency, registry response models, query encoding, timeout-based lookup, RegistryHit, and public module exposure.
Gateway hit provenance and fallback merging
crates/gateway-registry/src/search.rs, crates/gateway-registry/src/lib.rs
Adds local/registry hit metadata, fallback scoring, quota-limited merging, and related tests and exports.
Skill fallback metadata mapping
crates/skill-registry/Cargo.toml, crates/skill-registry/src/search.rs, crates/skill-registry/src/lib.rs
Adds the gateway dependency and maps registry installation and remote URL metadata into SkillHit results.
MCP search fallback dispatch
src/mcp_server.rs
Updates skill and tool searches to query the official registry when local results do not reach the requested limit.

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

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is a placeholder and omits the required Summary, Test plan, and Notes for reviewers sections. Replace it with the repository template and fill in Summary, Test plan checkboxes, and Notes for reviewers, including risks and compatibility.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly reflects the main change: adding fallback MCP registry search.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 task/60

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

@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: 6

🤖 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 `@crates/gateway-registry/src/registry_search.rs`:
- Line 8: Update the REGISTRY_BASE constant to use the stable /v0.1/servers
endpoint instead of /v0/servers, preserving the existing registry search and
listing behavior.
- Around line 64-68: Update the remote selection logic that consumes
RegistryRemote so it selects only entries with remote_type equal to
"streamable-http" before deriving remote_url, rather than taking the first
remote unconditionally. Preserve the existing URL handling for the selected
streamable HTTP remote and avoid surfacing sse entries.
- Around line 88-95: Update the registry request in the search flow around the
ureq::get call to use an explicit finite timeout that bounds both connection and
response-read operations. Preserve the existing empty-vector fallback for
request and JSON parsing failures.

In `@crates/gateway-registry/src/search.rs`:
- Around line 122-148: Update search_with_fallback to return
rusqlite::Result<Vec<ToolHit>> instead of Vec<ToolHit>, and propagate failures
from search with ?. Keep the existing local-result limit check and registry
fallback behavior for successful searches, returning the assembled results
through the Result type.

In `@crates/skill-registry/src/search.rs`:
- Around line 82-94: The synchronous remote fallback in search_with_fallback
must not run while SQLite mutexes are held. In
crates/skill-registry/src/search.rs lines 82-94, split local lookup from remote
supplementation or make the API asynchronous; in
crates/gateway-registry/src/registry.rs lines 178-182, release conn after local
search before invoking the remote phase; and in src/mcp_server.rs line 795, move
remote supplementation outside with_fresh_registry so skills_registry is
released before HTTP I/O.
- Around line 97-111: Update the SkillHit construction in the search result
mapping so fallback or remote registry hits use the stable registry source value
instead of an empty string. Change only the source field while preserving the
existing name, metadata, and install_hint mapping.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 11ec746d-b5fb-4813-8a39-faf9afbe03ae

📥 Commits

Reviewing files that changed from the base of the PR and between 5584110 and 67c06d7.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • crates/gateway-registry/Cargo.toml
  • crates/gateway-registry/src/lib.rs
  • crates/gateway-registry/src/registry.rs
  • crates/gateway-registry/src/registry_search.rs
  • crates/gateway-registry/src/search.rs
  • crates/skill-registry/Cargo.toml
  • crates/skill-registry/src/load.rs
  • crates/skill-registry/src/search.rs
  • src/mcp_server.rs

use crate::search::InstallHint;
use serde::Deserialize;

const REGISTRY_BASE: &str = "https://registry.modelcontextprotocol.io/v0/servers";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,220p' crates/gateway-registry/src/registry_search.rs

Repository: getappz/agentflare

Length of output: 4137


🌐 Web query:

MCP Registry search endpoint /v0/servers /v0.1/servers official documentation

💡 Result:

The Model Context Protocol (MCP) Registry provides a RESTful API for discovering and accessing MCP server metadata [1][2]. The official registry, hosted at https://registry.modelcontextprotocol.io, utilizes the /v0.1/servers endpoint for listing and searching servers [3][4][5]. There is no active /v0/servers endpoint in the current API specification; /v0.1/servers is the established standard for querying server information [3][4][5]. The GET /v0.1/servers endpoint supports the following query parameters to facilitate searching and filtering: - search: Performs a case-insensitive substring search on server names (e.g.,?search=filesystem) [3][6]. - updated_since: Filters servers updated after a specific RFC 3339 timestamp [3][4][6]. - version: Filters by version, supporting specific versions or the keyword latest [3][6]. - limit: Specifies the maximum number of items to return [4][6]. - cursor: Used for cursor-based pagination, allowing clients to retrieve subsequent pages of results [4][6]. Official documentation for the MCP Registry API, including the OpenAPI specification, can be found at the following resources: - Official Registry API Reference: https://registry.modelcontextprotocol.io/docs [7][8] - OpenAPI Specification: https://registry.modelcontextprotocol.io/openapi.yaml [9] - Registry GitHub Documentation: https://github.com/modelcontextprotocol/registry/tree/main/docs [2][10]

Citations:


Use the stable /v0.1/servers endpoint. The Registry API’s search/listing endpoint is /v0.1/servers; /v0 is the development API and can change or disappear, which would break fallback search.

🤖 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 `@crates/gateway-registry/src/registry_search.rs` at line 8, Update the
REGISTRY_BASE constant to use the stable /v0.1/servers endpoint instead of
/v0/servers, preserving the existing registry search and listing behavior.

Comment on lines +64 to +68
struct RegistryRemote {
#[serde(rename = "type")]
remote_type: String,
url: String,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- registry_search outline ---'
ast-grep outline crates/gateway-registry/src/registry_search.rs --view expanded || true

echo
echo '--- registry_search relevant lines ---'
sed -n '1,220p' crates/gateway-registry/src/registry_search.rs | cat -n

echo
echo '--- search RegistryRemote/remotes usage ---'
rg -n "RegistryRemote|remotes|streamable-http|sse|remote_type" crates/gateway-registry -S

Repository: getappz/agentflare

Length of output: 21407


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n--- file slice: registry_search.rs ---\n'
nl -ba crates/gateway-registry/src/registry_search.rs | sed -n '1,220p'

printf '\n--- all references to the selected remote_url / remote_type in gateway-registry ---\n'
rg -n "remote_url|remote_type|streamable-http|sse|remotes" crates/gateway-registry -S

Repository: getappz/agentflare

Length of output: 236


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n--- search.rs around RegistryHit usage ---\n'
python3 - <<'PY'
from pathlib import Path
p = Path('crates/gateway-registry/src/search.rs')
lines = p.read_text().splitlines()
for start, end in [(1,260)]:
    for i,l in enumerate(lines[start-1:end], start):
        print(f"{i:5} {l}")
PY

printf '\n--- registry.rs remote_url references ---\n'
python3 - <<'PY'
from pathlib import Path
p = Path('crates/gateway-registry/src/registry.rs')
lines = p.read_text().splitlines()
for i,l in enumerate(lines,1):
    if 'remote_url' in l or 'RegistryHit' in l or 'streamable-http' in l or 'sse' in l:
        lo = max(1, i-8); hi = min(len(lines), i+12)
        print(f"\n## around line {i}")
        for j in range(lo, hi+1):
            print(f"{j:5} {lines[j-1]}")
PY

printf '\n--- tests referencing remote_url ---\n'
rg -n "remote_url|streamable-http|sse" crates/gateway-registry/src crates/gateway-registry/tests -S

Repository: getappz/agentflare

Length of output: 27694


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n--- search.rs around RegistryHit usage ---\n'
python3 - <<'PY'
from pathlib import Path
p = Path('crates/gateway-registry/src/search.rs')
lines = p.read_text().splitlines()
for i,l in enumerate(lines,1):
    if 'RegistryHit' in l or 'remote_url' in l or 'Streamable HTTP URL' in l:
        lo = max(1, i-20)
        hi = min(len(lines), i+30)
        print(f"\n## around line {i}")
        for j in range(lo, hi+1):
            print(f"{j:5} {lines[j-1]}")
PY

printf '\n--- registry.rs remote_url references ---\n'
python3 - <<'PY'
from pathlib import Path
p = Path('crates/gateway-registry/src/registry.rs')
lines = p.read_text().splitlines()
for i,l in enumerate(lines,1):
    if 'remote_url' in l or 'RegistryHit' in l or 'streamable-http' in l or 'sse' in l:
        lo = max(1, i-12)
        hi = min(len(lines), i+20)
        print(f"\n## around line {i}")
        for j in range(lo, hi+1):
            print(f"{j:5} {lines[j-1]}")
PY

printf '\n--- tests referencing remote_url or transport ---\n'
rg -n "remote_url|streamable-http|sse|remotes" crates/gateway-registry/src crates/gateway-registry/tests -S

Repository: getappz/agentflare

Length of output: 27060


🌐 Web query:

MCP Registry remote servers streamable-http sse remotes type documentation

💡 Result:

In the Model Context Protocol (MCP) registry, remote servers are configured using a server.json file which includes a remotes property to define how clients connect to the server [1][2]. This property accepts the type of transport, specifically streamable-http or sse [1][3]. Streamable HTTP (introduced in the 2025-03-26 protocol revision) is the modern, recommended transport for remote MCP servers [4][5]. It is designed to be stateless and uses a single HTTP endpoint, making it well-suited for load balancing and serverless deployment environments [5]. It replaces the older HTTP+SSE transport (from 2024-11-05), which has been deprecated [4]. SSE (Server-Sent Events) is an earlier transport mechanism that typically requires maintaining long-lived connections, which can be more complex to deploy behind standard web infrastructure compared to the single-endpoint streamable-http design [5]. Key documentation and technical resources: - Registry Configuration: The official MCP registry documentation details how to specify these transports within the server.json file [1][2]. It supports variable templating (e.g., for multi-tenant setups) within the URL property of the remotes configuration [2]. - Implementation: The MCP TypeScript SDK provides a WebStandardStreamableHTTPServerTransport class that allows servers to implement the Streamable HTTP specification using standard Web APIs [6]. - Migration: Current specifications advise that new implementations should use Streamable HTTP, while existing implementations should migrate away from the deprecated HTTP+SSE transport [4]. Clients may attempt to support both for backward compatibility by checking for specific HTTP status codes or endpoints, though this is considered legacy behavior [4]. Top results: [1][4][2][5]

Citations:


Select the streamable-http remote server.remotes.into_iter().next() drops remote_type, so an sse entry can be surfaced as remote_url. Filter on remote_type == "streamable-http" or carry the transport with the URL.

🤖 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 `@crates/gateway-registry/src/registry_search.rs` around lines 64 - 68, Update
the remote selection logic that consumes RegistryRemote so it selects only
entries with remote_type equal to "streamable-http" before deriving remote_url,
rather than taking the first remote unconditionally. Preserve the existing URL
handling for the selected streamable HTTP remote and avoid surfacing sse
entries.

Comment thread crates/gateway-registry/src/registry_search.rs Outdated
Comment thread crates/gateway-registry/src/search.rs Outdated
Comment on lines +122 to +148
pub fn search_with_fallback(
conn: &Connection,
query: &str,
limit: usize,
mode: MatchMode,
) -> Vec<ToolHit> {
let local = search(conn, query, limit, mode).unwrap_or_default();
if local.len() >= limit {
return local;
}
let remaining = limit.saturating_sub(local.len());
let registry = search_registry(query, remaining);
let mut results = local;
for hit in registry {
results.push(ToolHit {
server: hit.server,
tool: String::new(),
description: hit.description,
input_schema: Value::Null,
score: hit.score,
source: HitSource::Registry,
install_hint: hit.install_hint,
remote_url: hit.remote_url,
});
}
results
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Preserve local search errors instead of treating them as zero hits.

Line 128 converts database failures into successful remote fallback results. This hides index corruption/query failures and prevents the gateway and MCP layers from using their existing error-conversion paths. Return rusqlite::Result<Vec<ToolHit>> and apply ? to search.

Proposed fix
 pub fn search_with_fallback(
     conn: &Connection,
     query: &str,
     limit: usize,
     mode: MatchMode,
-) -> Vec<ToolHit> {
-    let local = search(conn, query, limit, mode).unwrap_or_default();
+) -> rusqlite::Result<Vec<ToolHit>> {
+    let local = search(conn, query, limit, mode)?;
     if local.len() >= limit {
-        return local;
+        return Ok(local);
     }
...
-    results
+    Ok(results)
 }
📝 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
pub fn search_with_fallback(
conn: &Connection,
query: &str,
limit: usize,
mode: MatchMode,
) -> Vec<ToolHit> {
let local = search(conn, query, limit, mode).unwrap_or_default();
if local.len() >= limit {
return local;
}
let remaining = limit.saturating_sub(local.len());
let registry = search_registry(query, remaining);
let mut results = local;
for hit in registry {
results.push(ToolHit {
server: hit.server,
tool: String::new(),
description: hit.description,
input_schema: Value::Null,
score: hit.score,
source: HitSource::Registry,
install_hint: hit.install_hint,
remote_url: hit.remote_url,
});
}
results
}
pub fn search_with_fallback(
conn: &Connection,
query: &str,
limit: usize,
mode: MatchMode,
) -> rusqlite::Result<Vec<ToolHit>> {
let local = search(conn, query, limit, mode)?;
if local.len() >= limit {
return Ok(local);
}
let remaining = limit.saturating_sub(local.len());
let registry = search_registry(query, remaining);
let mut results = local;
for hit in registry {
results.push(ToolHit {
server: hit.server,
tool: String::new(),
description: hit.description,
input_schema: Value::Null,
score: hit.score,
source: HitSource::Registry,
install_hint: hit.install_hint,
remote_url: hit.remote_url,
});
}
Ok(results)
}
🤖 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 `@crates/gateway-registry/src/search.rs` around lines 122 - 148, Update
search_with_fallback to return rusqlite::Result<Vec<ToolHit>> instead of
Vec<ToolHit>, and propagate failures from search with ?. Keep the existing
local-result limit check and registry fallback behavior for successful searches,
returning the assembled results through the Result type.

Comment thread crates/skill-registry/src/search.rs Outdated
Comment thread crates/skill-registry/src/search.rs Outdated
Comment on lines +97 to +111
results.push(SkillHit {
name: hit.server,
source: String::new(),
description: hit.description,
est_tokens: 0,
compressed: false,
score: hit.score,
install_hint: hit.install_hint.map(|h| {
if let Some(runtime) = h.runtime_hint {
format!("{} {}", runtime, h.identifier)
} else {
format!("{}:{}", h.registry_type, h.identifier)
}
}),
remote_url: hit.remote_url,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Mark fallback hits as registry-sourced.

source: String::new() drops provenance for remote results. Clients cannot reliably distinguish these non-loadable registry entries from local skills; emit a stable registry source value.

Suggested change
-            source: String::new(),
+            source: "mcp_registry".to_string(),
📝 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
results.push(SkillHit {
name: hit.server,
source: String::new(),
description: hit.description,
est_tokens: 0,
compressed: false,
score: hit.score,
install_hint: hit.install_hint.map(|h| {
if let Some(runtime) = h.runtime_hint {
format!("{} {}", runtime, h.identifier)
} else {
format!("{}:{}", h.registry_type, h.identifier)
}
}),
remote_url: hit.remote_url,
results.push(SkillHit {
name: hit.server,
source: "mcp_registry".to_string(),
description: hit.description,
est_tokens: 0,
compressed: false,
score: hit.score,
install_hint: hit.install_hint.map(|h| {
if let Some(runtime) = h.runtime_hint {
format!("{} {}", runtime, h.identifier)
} else {
format!("{}:{}", h.registry_type, h.identifier)
}
}),
remote_url: hit.remote_url,
🤖 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 `@crates/skill-registry/src/search.rs` around lines 97 - 111, Update the
SkillHit construction in the search result mapping so fallback or remote
registry hits use the stable registry source value instead of an empty string.
Change only the source field while preserving the existing name, metadata, and
install_hint mapping.

@getappz

getappz commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

Re-reviewing (same commit `67c06d74` I looked at earlier as the task/60 worktree, now formalized here) — none of my prior findings have changed since the code is byte-identical:

  1. Real bug — un-timed-out network call held under the skills-registry mutex. `AgentflareMcp::with_fresh_registry` locks `self.skills_registry` (shared across the whole session) for the entire duration of the closure. `skill`'s `search` action calls `reg.search_with_fallback(...)`, which — when local results are short — calls a blocking `ureq::get` with no timeout configured. A slow/unreachable `registry.modelcontextprotocol.io` hangs the `skill` tool (search and load) for the whole session, for as long as that request takes.
  2. Dead code with the same hazard baked in. `gateway_registry::Registry::search_with_fallback` (`crates/gateway-registry/src/registry.rs`) is added but never called — the gateway/`tool_search` path still uses plain `reg.search(...)`. It also locks `self.conn: Mutex` across the network call, so it inherits bug Bump actions/download-artifact from 4 to 8 #1 if someone wires it up later expecting it's safe.
  3. Score semantics are inconsistent with the doc comment. Local `search()` orders by `bm25(...)` ascending (lower/negative = better match), but the fallback appends registry hits with a flat `score: 0.5` and claims that's "lower... so local results outrank them" — backwards, since 0.5 > every negative local score. Currently harmless only because of append-order, not actual score value; will invert if anything ever sorts by `score`. Duplicated in `crates/skill-registry/src/search.rs`.

Also: this PR is now genuinely conflicting with `master` (`mergeStateStatus: DIRTY`) — `master` picked up the FTS5/BM25 consolidation into `flare-search-kit` (#189) which touched the same `search.rs` files this PR modifies. Will need a rebase/merge regardless of the above.

Recommend fixing #1 (timeout + don't hold the lock across I/O) before merge.

- Don't hold any lock (skills_registry mutex or gateway Registry's conn
  mutex) across the fallback HTTP call to registry.modelcontextprotocol.io:
  local search now runs and releases its lock before the network fallback
  fires, in both the skill and tool MCP actions.
- Add a 3s timeout to the registry HTTP request so a slow/unreachable
  registry can't hang a search indefinitely.
- Wire the fallback into tool_search (the "tool"/"search" MCP action) too,
  matching the item's original scope -- gateway_registry::Registry's
  fallback method existed but was dead code, never called anywhere.
- Fix score semantics: local bm25() scores are ascending (lower = better,
  no DESC), but registry hits got a flat 0.5 with a comment claiming that
  ranked them below local results -- backwards, since 0.5 > every negative
  local score. Registry hits now score f64::MAX, so they always sort last
  under the same ascending convention if the merged list is ever re-sorted.
- Resolve merge conflicts from master's flare-search-kit consolidation
  (#189) by rebuilding gateway-registry/search.rs and skill-registry's
  merge logic against the new shared MatchMode/fts_query/clamped_limit.

@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)
crates/gateway-registry/src/registry_search.rs (1)

117-117: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remote selection still ignores transport type.

server.remotes.into_iter().next().map(|r| r.url) picks the first remote regardless of remote_type, so an sse entry can be surfaced as remote_url even though the field is documented as a Streamable HTTP URL. Flagged on a previous commit; still unaddressed.

🐛 Proposed fix
-            let remote_url = server.remotes.into_iter().next().map(|r| r.url);
+            let remote_url = server
+                .remotes
+                .into_iter()
+                .find(|r| r.remote_type == "streamable-http")
+                .map(|r| r.url);
🤖 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 `@crates/gateway-registry/src/registry_search.rs` at line 117, Update remote
selection at the `server.remotes` assignment to filter entries by the supported
Streamable HTTP transport type before extracting `url`, rather than taking the
first remote unconditionally. Preserve `remote_url` as `None` when no matching
remote exists.
♻️ Duplicate comments (1)
crates/gateway-registry/src/registry_search.rs (1)

9-9: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Still using the unstable /v0 endpoint.

REGISTRY_BASE remains .../v0/servers. Per the MCP Registry docs, /v0 is explicitly called unstable/deprecated in favor of /v0.1/servers; this was already flagged on a previous commit and remains unaddressed.

🐛 Proposed fix
-const REGISTRY_BASE: &str = "https://registry.modelcontextprotocol.io/v0/servers";
+const REGISTRY_BASE: &str = "https://registry.modelcontextprotocol.io/v0.1/servers";
🤖 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 `@crates/gateway-registry/src/registry_search.rs` at line 9, Update the
REGISTRY_BASE constant to use the stable MCP Registry `/v0.1/servers` endpoint
instead of the deprecated `/v0/servers` path.
🤖 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 `@crates/skill-registry/src/search.rs`:
- Around line 62-85: Update merge_registry_hits when constructing
registry-fallback SkillHit values so source is populated with the established
registry provenance identifier instead of an empty string. Preserve the existing
local-hit sources and all other fallback-hit mapping behavior.

In `@src/mcp_server.rs`:
- Around line 1969-1983: The registry fallback branch in tool() must not execute
synchronous search_registry on a Tokio worker thread. In src/mcp_server.rs lines
1969-1983, wrap the fallback lookup and registry hit merge in
tokio::task::spawn_blocking, await its result, and propagate both join and
search errors while preserving the existing local-hit and limit behavior; apply
the same handling at the sibling call site in src/mcp_server.rs lines 794-805.

---

Outside diff comments:
In `@crates/gateway-registry/src/registry_search.rs`:
- Line 117: Update remote selection at the `server.remotes` assignment to filter
entries by the supported Streamable HTTP transport type before extracting `url`,
rather than taking the first remote unconditionally. Preserve `remote_url` as
`None` when no matching remote exists.

---

Duplicate comments:
In `@crates/gateway-registry/src/registry_search.rs`:
- Line 9: Update the REGISTRY_BASE constant to use the stable MCP Registry
`/v0.1/servers` endpoint instead of the deprecated `/v0/servers` path.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d59f14ce-5002-4663-81a1-101016a53431

📥 Commits

Reviewing files that changed from the base of the PR and between 67c06d7 and 9df3e86.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • crates/gateway-registry/Cargo.toml
  • crates/gateway-registry/src/lib.rs
  • crates/gateway-registry/src/registry_search.rs
  • crates/gateway-registry/src/search.rs
  • crates/skill-registry/Cargo.toml
  • crates/skill-registry/src/lib.rs
  • crates/skill-registry/src/search.rs
  • src/mcp_server.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/skill-registry/Cargo.toml

Comment on lines +62 to +85
pub fn merge_registry_hits(
mut local: Vec<SkillHit>,
limit: usize,
registry: Vec<gateway_registry::registry_search::RegistryHit>,
) -> Vec<SkillHit> {
let remaining = limit.saturating_sub(local.len());
local.extend(registry.into_iter().take(remaining).map(|hit| SkillHit {
name: hit.server,
source: String::new(),
description: hit.description,
est_tokens: 0,
compressed: false,
score: gateway_registry::REGISTRY_FALLBACK_SCORE,
install_hint: hit.install_hint.map(|h| {
if let Some(runtime) = h.runtime_hint {
format!("{} {}", runtime, h.identifier)
} else {
format!("{}:{}", h.registry_type, h.identifier)
}
}),
remote_url: hit.remote_url,
}));
local
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Registry-fallback hits still lose provenance (source: String::new()).

Flagged on a previous commit and still present: local SkillHits carry a real source (e.g. "claude-user"), but registry-fallback hits get an empty string, so clients can't tell these apart from a local skill with a blank source.

🐛 Proposed fix
     local.extend(registry.into_iter().take(remaining).map(|hit| SkillHit {
         name: hit.server,
-        source: String::new(),
+        source: "mcp_registry".to_string(),
         description: hit.description,
🤖 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 `@crates/skill-registry/src/search.rs` around lines 62 - 85, Update
merge_registry_hits when constructing registry-fallback SkillHit values so
source is populated with the established registry provenance identifier instead
of an empty string. Preserve the existing local-hit sources and all other
fallback-hit mapping behavior.

Comment thread src/mcp_server.rs
Comment on lines +1969 to +1983
let limit = req.limit.unwrap_or(5);
let local = {
let guard = self.ensure_gateway_registry().await?;
let reg = guard.as_ref().expect("ensured above");
reg.search(&query, limit, mode)
.map_err(|e| ErrorData::internal_error(e.to_string(), None))?
};
let hits = if local.len() < limit {
let remaining = limit - local.len();
let registry =
gateway_registry::registry_search::search_registry(&query, remaining);
gateway_registry::merge_registry_hits(local, limit, registry)
} else {
local
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== search_registry definitions and call sites ==\n'
rg -n "fn search_registry|search_registry\(" src | sed -n '1,200p'

printf '\n== mcp_server.rs around reported lines ==\n'
sed -n '760,830p' src/mcp_server.rs
printf '\n---\n'
sed -n '1940,2005p' src/mcp_server.rs

printf '\n== gateway_registry modules that may define search_registry ==\n'
fd -a "registry_search" src || true
fd -a "gateway_registry" src || true

Repository: getappz/agentflare

Length of output: 7012


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== registry_search implementation =='
file=$(fd -a "registry_search" src | head -n 1)
if [ -n "${file:-}" ]; then
  wc -l "$file"
  sed -n '1,220p' "$file"
fi

echo '== mcp_server.rs function signatures around the affected code =='
sed -n '740,815p' src/mcp_server.rs
echo '---'
sed -n '1940,1995p' src/mcp_server.rs

Repository: getappz/agentflare

Length of output: 6551


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== search_registry occurrences across repo =='
rg -n "search_registry" .

echo
echo '== registry_search / ureq / blocking client hints =='
rg -n "ureq|reqwest::blocking|blocking::Client|timeout" . \
  -g '!target' -g '!node_modules' -g '!dist' -g '!build'

Repository: getappz/agentflare

Length of output: 22744


Offload the registry fallback lookup in tool() src/mcp_server.rs#L1969-L1983 still calls gateway_registry::registry_search::search_registry inline; that function uses synchronous ureq with a 3s timeout, so each miss can block a Tokio worker thread. Move this branch to tokio::task::spawn_blocking.

📍 Affects 1 file
  • src/mcp_server.rs#L1969-L1983 (this comment)
  • src/mcp_server.rs#L794-L805
🤖 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.rs` around lines 1969 - 1983, The registry fallback branch in
tool() must not execute synchronous search_registry on a Tokio worker thread. In
src/mcp_server.rs lines 1969-1983, wrap the fallback lookup and registry hit
merge in tokio::task::spawn_blocking, await its result, and propagate both join
and search errors while preserving the existing local-hit and limit behavior;
apply the same handling at the sibling call site in src/mcp_server.rs lines
794-805.

@getappz
getappz merged commit 1b25586 into master Jul 15, 2026
20 of 23 checks passed
@getappz
getappz deleted the task/60 branch July 15, 2026 08:09
getappz added a commit that referenced this pull request Jul 15, 2026
- Fix the MCP Registry endpoint: /v0/servers is not a real endpoint,
  registry.modelcontextprotocol.io only serves /v0.1/servers -- the
  fallback search has been silently hitting a dead URL and returning
  nothing since it merged.
- Select only the "streamable-http" remote when picking remote_url,
  instead of blindly taking the first one -- a deprecated HTTP+SSE
  remote could be surfaced instead, which this codebase can't speak to.
  Extracted select_remote_url() so this is unit-testable.
- Give skill-registry's fallback hits a real source ("mcp_registry")
  instead of an empty string, so clients can tell them apart from a
  local skill that happens to have a blank source.
- Move the registry HTTP call off the async runtime: skill()/tool()'s
  search actions now run search_registry via tokio::task::spawn_blocking
  instead of calling the synchronous ureq request inline. The prior fix
  already stopped holding a lock across that call, but the main binary's
  tokio runtime only enables the "rt" feature (single-threaded), so the
  blocking call itself was still stalling every other in-flight request
  for up to 3s, not just ones contending for the registry lock.
getappz added a commit that referenced this pull request Aug 26, 2026
…ly mentions the item number (#603)

* Fixed find_by_item_marker false-positive (item #190): added PullRequest.body field, filter search hits locally against the literal pr_footer suffix instead of trusting GitHub search phrase-match; regression test added; 17/17 pulls tests pass.

Agentflare-Branch: task/190-duplicate-work-preflight-check-false-pos
Agentflare-Item: 190-duplicate-work-preflight-check-false-pos

* chore: retrigger CI (CodeQL runner failed to acquire a hosted runner, transient infra failure not a code issue)

Agentflare-Agent: claude-code
Agentflare-Branch: task/190-duplicate-work-preflight-check-false-pos
Agentflare-Item: 190
Agentflare-Session: c5a4ab79-7ae7-4faf-b526-71ee9f9b5e37

* fix: remove duplicate PullRequest.body field

GitHub's server-side branch-update merge silently produced a real
compile error: item #70's fix (PR #608) and this item's own fix both
independently added a body: Option<String> field to PullRequest for
the same reason (marks_item-based ownership verification), at
different insertion points. A textual 3-way merge can't detect that
as a semantic duplicate -- it merged both additions cleanly with no
conflict markers, leaving two body fields on the same struct
(E0124/E0062). CI's earlier green build/clippy checkmarks predate
this merge landing; the field duplication was never actually built
against.

Kept the single field (with the more complete doc comment), verified
both call sites (github::pulls::marks_item/find_by_item_marker and
worktree.rs's four PR-ownership checks) already use it identically
via pr.body.as_deref() -- the two fixes are complementary, not
conflicting, once deduplicated.

Verified: cargo build/clippy/fmt clean against CI's exact invocations;
22 github::pulls + 14 worktree:: + 2 item_pr_failure + 246 mcp_server
tests all pass.

Agentflare-Branch: task/190-duplicate-work-preflight-check-false-pos
Agentflare-Item: 190-duplicate-work-preflight-check-false-pos

* chore: retrigger CI (ci workflow never fired for b341c3e — webhook delivery gap, not a code/filter issue)

Agentflare-Agent: claude-code
Agentflare-Branch: task/190-duplicate-work-preflight-check-false-pos
Agentflare-Item: 190
Agentflare-Session: c5a4ab79-7ae7-4faf-b526-71ee9f9b5e37

---------

Co-authored-by: shiva <shiva@gosysinfo.tech>
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