Skip to content

doctor: linkedin/exa_search report 'ok' based on cwd name (substring match against config path) #508

Description

@NOOBUV

Summary

doctor reports linkedin and exa_search as ok based on the current working directory, not on whether the MCP is actually configured. Both checks grep the raw output of mcporter config list for a bare substring, and that output contains the project config path — which embeds $CWD. Any directory whose name contains the substring flips the channel to healthy.

exa_search is the worse of the two: it greps for "exa", which is a substring of the very common example.

Reproduction

With no MCP servers configured at all (mcporter config listNo local servers match the provided filters.):

$ mkdir -p /tmp/example-project && cd /tmp/example-project
$ agent-reach doctor --json | jq -r '.exa_search | "\(.status) \(.active_backend)"'
ok Exa via mcporter          # ← FALSE: Exa is not configured

$ mkdir -p /tmp/neutral-dir && cd /tmp/neutral-dir
$ agent-reach doctor --json | jq -r '.exa_search | "\(.status) \(.active_backend)"'
off null                     # ← correct

Same for linkedin from any *linkedin* directory:

$ cd ~/Desktop/linkedin-pipeline
$ agent-reach doctor --json | jq -r '.linkedin.status'
ok                           # ← FALSE
$ cd /tmp && agent-reach doctor --json | jq -r '.linkedin.status'
off                          # ← correct

Root cause

mcporter config list always prints the project config path, even when nothing is configured:

No local servers match the provided filters.

Project config: /tmp/example-project/config/mcporter.json (missing)
System config: /Users/me/.mcporter/mcporter.json (missing)

The checks substring-match that whole blob:

  • agent_reach/channels/linkedin.py:36if "linkedin" in probe.output.lower():
  • agent_reach/channels/exa_search.py:34if "exa" in probe.output.lower():

So Project config: /tmp/example-project/... satisfies "exa" in output.

Impact

doctor is documented as the routing source of truth ("动手前先体检 … 按各平台 active_backend 字段选命令组"). A false ok sends the agent down a command path whose backend was never installed, and the resulting failure looks like a broken MCP rather than an unconfigured one. This cost a real debugging session: doctor said linkedin: ok, but mcporter list reported no servers configured — the channel had simply never been set up, and the project directory happened to be named linkedin-pipeline.

Suggested fix

Parse the server list rather than grepping the whole blob. mcporter config list --json returns structured output; match on a server name/key, and scope the match to the servers section instead of the config-path lines. Failing that, at minimum strip the Project config: / System config: trailer before matching, and use a word-boundary/exact-name comparison rather than a bare substring.

A regression test that runs doctor from a tmp_path named example-linkedin with an empty mcporter config would pin both cases.


Second, unrelated issue: references/career.md LinkedIn instructions are stale

references/career.md (and the same hints in channels/linkedin.py) tell the user:

pip install linkedin-scraper-mcp
mcporter config add linkedin http://localhost:3000/mcp

Both lines are dead:

  1. linkedin-scraper-mcp was renamed. On PyPI it is now a transitional stub whose own summary reads "Renamed to mcp-server-linkedin. This transitional package forwards to it." The live package is mcp-server-linkedin (currently 4.17.0). Upstream (https://github.com/stickerdaniel/linkedin-mcp-server) recommends uvx, not pip.
  2. The transport is wrong. The server is stdio by default; http://localhost:3000/mcp assumes an HTTP server nobody started. The port is also wrong — --transport streamable-http defaults to :8000/mcp.
  3. The doc's example selectors (linkedin-scraper.get_person_profile) don't match the server name in its own config add line (linkedin), and search_people is listed as in-development upstream.

What actually works today:

mcporter config add linkedin --command uvx --arg 'mcp-server-linkedin@latest' \
  --env UV_HTTP_TIMEOUT=300 --scope home
uvx mcp-server-linkedin@latest --import-from-browser chrome   # or --login
mcporter call 'linkedin.get_feed(num_posts: 5)'

Note mcporter config add defaults to --scope project, which writes into $CWD/config/mcporter.json — worth pinning --scope home in the docs so the channel resolves from any directory. (That default is also what makes the substring bug above so easy to hit.)

Happy to send a PR for either half if useful.

Found while wiring the LinkedIn channel on v1.5.0, macOS, Python 3.14.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions