# How the LinkedIn Channel Routes Requests Between linkedin-mcp and Jina Reader Backends

> Learn how the LinkedIn channel routes requests to linkedin-mcp or Jina Reader backends. Discover how the active backend attribute directs traffic for your Agent-Reach integration.

- Repository: [Pnant/Agent-Reach](https://github.com/Panniantong/Agent-Reach)
- Tags: internals
- Published: 2026-07-12

---

**The LinkedIn channel automatically routes requests to the linkedin-scraper-mcp backend when the mcporter CLI is detected and configured, otherwise falling back to Jina Reader, with the `active_backend` attribute determining the runtime dispatch path.**

The Agent-Reach repository implements a robust LinkedIn integration that transparently switches between two distinct backends. Understanding how the LinkedIn channel routes requests between the linkedin-mcp and Jina Reader backends reveals a sophisticated health-check mechanism that prioritizes structured data extraction while maintaining universal accessibility.

## Backend Declaration and MCP Health Detection

### Declaring Available Backends

In [`agent_reach/channels/linkedin.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/linkedin.py), the `LinkedInChannel` class explicitly declares its supported backends:

```python
backends = ["linkedin-scraper-mcp", "Jina Reader"]

```

This ordered list establishes the preference hierarchy, with the MCP server offering richer structured data and Jina Reader providing a reliable fallback.

### Probing the MCP Server

The `check()` method determines availability by probing the local **mcporter** installation using the `probe_command` utility from [`agent_reach/probe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/probe.py):

```python
probe = probe_command("mcporter", ["config", "list"], timeout=10, package="mcporter")

```

If the probe succeeds and the output contains the string "linkedin", the channel sets:

```python
self.active_backend = "linkedin-scraper-mcp"

```

Otherwise, the channel remains operational but relies on the Jina Reader fallback, ensuring continuous functionality even without the MCP server installed.

## Runtime Routing Logic in BaseChannel

The actual request dispatch occurs in the `BaseChannel` class defined in [`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py). When `read()` or `search()` methods are invoked, the base class inspects `self.active_backend` to determine which concrete implementation to execute.

**linkedin-scraper-mcp path**: When `active_backend` equals "linkedin-scraper-mcp", the channel delegates to the MCP server via the `run_backend` helper, executing structured profile extraction through the npm-based MCP server.

**Jina Reader fallback**: If `active_backend` is unset or the MCP probe failed, requests route to the **Jina Reader** API, which fetches raw HTML and extracts textual content without requiring local MCP infrastructure.

## Practical Implementation Examples

The following examples demonstrate how the routing operates in practice:

```python

# Automatic backend selection

from agent_reach.core import AgentReach

ar = AgentReach()
profile = ar.read("https://www.linkedin.com/in/jane-doe-12345")
print(profile)  # Structured data if MCP active, plain text otherwise

```

For advanced use cases, you can inspect the backend selection explicitly:

```python
from agent_reach.channels.linkedin import LinkedInChannel

chan = LinkedInChannel()
chan.check()  # Detects active_backend based on mcporter probe

if chan.active_backend == "linkedin-scraper-mcp":
    data = chan.read("https://www.linkedin.com/jobs/view/123456")
else:
    data = chan.read("https://www.linkedin.com/jobs/view/123456")
print(data)

```

## Key Source Files and Responsibilities

- **[`agent_reach/channels/linkedin.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/linkedin.py)**: Defines the `LinkedInChannel` class, declares the `backends` list, and implements the `check()` method that sets `active_backend` based on mcporter probe results.

- **[`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py)**: Contains the generic `BaseChannel` class that routes requests to the backend specified in `self.active_backend`.

- **[`agent_reach/probe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/probe.py)**: Provides the `probe_command` utility used to detect external tool availability and configuration.

- **[`agent_reach/core.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/core.py)**: Exposes the public `AgentReach` facade that forwards requests to the appropriate channel.

## Summary

- The LinkedIn channel maintains a `backends` list prioritizing "linkedin-scraper-mcp" over "Jina Reader".
- The `check()` method probes mcporter via `probe_command` to determine if the MCP server is configured for LinkedIn scraping.
- The `active_backend` attribute stores the runtime selection, driving the routing decision in `BaseChannel`.
- Requests automatically fall back to Jina Reader when the MCP server is unavailable, ensuring graceful degradation.
- All routing logic is implemented in the open-source Agent-Reach repository under the `agent_reach/channels/` directory.

## Frequently Asked Questions

### What triggers the fallback to Jina Reader?

The fallback activates when the `probe_command` call to mcporter fails, returns no output, or lacks the "linkedin" configuration string. In these cases, `active_backend` remains unset or inaccessible, causing `BaseChannel` to route requests to the Jina Reader API instead of attempting MCP server communication.

### How does the channel detect if linkedin-mcp is available?

Detection occurs through the `check()` method in `LinkedInChannel`, which executes `probe_command("mcporter", ["config", "list"], timeout=10, package="mcporter")`. If this command exits successfully and its output contains the substring "linkedin", the channel sets `active_backend = "linkedin-scraper-mcp"` and subsequent requests use the MCP backend.

### Can I force the channel to use a specific backend?

While the public `AgentReach` API abstracts backend selection, you can instantiate `LinkedInChannel` directly and manually assign `chan.active_backend = "linkedin-scraper-mcp"` or `"Jina Reader"` before calling `read()` or `search()`. Note that manual override bypasses the automatic health checking provided by the `check()` method.

### Where is the routing logic implemented?

The routing logic spans two files: [`agent_reach/channels/linkedin.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/linkedin.py) sets the `active_backend` attribute during initialization based on the mcporter probe, while [`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py) implements the actual dispatch logic that reads this attribute and delegates to the appropriate backend handler.