Metadata-Version: 2.5
Name: trialmatch-mcp-bridge
Version: 0.1.2
Summary: Local stdio<->HTTPS bridge for Claude Desktop, per THI-882. Auth + relay only — no tool logic; the real MCP server lives in ../src/trialmatch_criteria_mcp, deployed to AgentCore Runtime behind the Gateway this bridge talks to.
Requires-Python: >=3.12
Requires-Dist: httpx>=0.27
Requires-Dist: mcp<3,>=2.0
Requires-Dist: pydantic-settings>=2.3
Requires-Dist: pydantic>=2.7
Description-Content-Type: text/markdown

# trialmatch-mcp-bridge

A local stdio↔HTTPS bridge for Claude Desktop (THI-882). Deliberately **not** the real MCP
server — no tool logic here at all. It does PKCE + local-loopback login against Cognito, caches
a refresh token, and relays every MCP call from Desktop to the AgentCore Gateway over HTTPS with
`Authorization: Bearer <token>` attached. The actual tools live in
[`../src/trialmatch_criteria_mcp/`](../src/trialmatch_criteria_mcp/), deployed to AgentCore
Runtime behind that Gateway.

## Setup

**Published to PyPI** — this is the recommended way to run it, no repo access or git credentials
needed at all:

```bash
uvx trialmatch-mcp-bridge --login   # one-time interactive sign-in; opens your browser
```

This matters beyond convenience: some MCP hosts run their server subprocesses in a sandbox that
can't reach your normal git credentials — confirmed live, Claude Desktop (an MSIX/Windows-Store
packaged app) runs in an AppContainer that can't access the interactive user's `gh`-configured
git credential helper, so a `git+https://` dependency against this (private) repo fails there
even though it works fine from a plain terminal on the same machine. A public PyPI package has
no credential step to fail.

Alternative, if you're already working in a local clone of this repo:

```bash
cd bridge
uv sync
uv run trialmatch-mcp-bridge --login
```

Either way, this caches a refresh token at `~/.trialmatch-mcp/credentials.json` (0600).
Subsequent runs refresh silently — you shouldn't need `--login` again unless the refresh token
itself expires or is revoked.

## Claude Desktop / Claude Code configuration

Add to `claude_desktop_config.json` (Desktop) or `.mcp.json` (Code):

```json
{
  "mcpServers": {
    "trialmatch-criteria": {
      "command": "uvx",
      "args": ["trialmatch-mcp-bridge"]
    }
  }
}
```

Or, from a local clone:

```json
{
  "mcpServers": {
    "trialmatch-criteria": {
      "command": "uv",
      "args": ["run", "--directory", "/absolute/path/to/TrialMatch-Criteria-MCP/bridge", "trialmatch-mcp-bridge"]
    }
  }
}
```

(Or point `command` at the installed `trialmatch-mcp-bridge` console script directly if you've
installed this package outside a `uv`-managed venv.)

**Not** Claude Code's built-in remote-MCP OAuth support (`/mcp add <gateway-url>` or
`.mcp.json`'s `"type": "http"` + `oauth` block) — that connects directly to the Gateway and lets
Claude Code do its own OAuth against Cognito, which currently hits a known Claude Code bug
([anthropics/claude-code#35846](https://github.com/anthropics/claude-code/issues/35846)):
Cognito's discovery document doesn't advertise `code_challenge_methods_supported` (it supports
PKCE S256 fine, it just doesn't list it there), and Claude Code's token exchange breaks as a
result. This bridge sidesteps that entirely by doing PKCE itself, confirmed working.

## Publishing

Automatic, via `.github/workflows/ci.yml`'s `publish-bridge` job — no manual version bump, no
git tags, no stored PyPI token (uses PyPI's OIDC "Trusted Publishing"). On a merge to `main`
that touches `bridge/**`: bumps the PATCH version (computed from PyPI's own current "latest",
not from anything in this repo) and publishes it.

Gated behind the `pypi` GitHub Environment (repo Settings → Environments → `pypi`), which has a
deployment-branch policy restricting it to `main` only — per PyPI's own Trusted Publishing
guidance, a dedicated environment for the publishing workflow is "strongly encouraged,
especially if your repository has maintainers with commit access who shouldn't have PyPI
publishing access." (No pre-release/dev-version channel — deliberately kept to just this one
path; testing a specific in-progress change is simpler done straight from a local clone, see
Setup above, than by publishing and pinning a throwaway version.)

One-time setup (already done, noted here in case the project is ever re-created):
1. Create the `pypi` GitHub Environment with a deployment-branch policy restricting it to
   `main` (done via `gh api repos/Third-Opinion/TrialMatch-Criteria-MCP/environments/pypi`, no
   UI needed — see git history for the exact call).
2. A PyPI account with access registers a pending trusted publisher at
   https://pypi.org/manage/account/publishing/ for project `trialmatch-mcp-bridge`, owner
   `Third-Opinion`, repo `TrialMatch-Criteria-MCP`, workflow `ci.yml`, environment name `pypi`.

## Test

```bash
uv run pytest
uv run ruff check . && uv run ruff format --check .
uv run mypy src
```

Unit tests mock the Cognito token endpoint and the loopback callback — no browser or real
Cognito pool needed to run them. A real end-to-end test still needs an actual browser login
once (`--login`), since PKCE Authorization Code flow is inherently interactive by design.
