Metadata-Version: 2.4
Name: allye-mcp
Version: 1.10.0
Summary: Fênix Cloud MCP server implemented in Python
Author: Allye Inc
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: pydantic>=2.5
Requires-Dist: requests>=2.31
Requires-Dist: urllib3>=2.0
Requires-Dist: aiohttp>=3.9
Requires-Dist: pydantic-settings>=2.0
Requires-Dist: python-json-logger>=2.0
Requires-Dist: PyJWT[crypto]>=2.8
Provides-Extra: dev
Requires-Dist: pytest>=7.4; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: black>=23.0; extra == "dev"
Requires-Dist: flake8>=6.0; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Requires-Dist: twine>=4.0; extra == "dev"

<p align="center">
  <img src="https://allye.devshire.app/logos/logo_allye.png" alt="Allye MCP" width="200" />
</p>

<p align="center">
  <strong>Allye MCP Server</strong><br/>
  Python MCP server for Allye Cloud API integration
</p>

<p align="center">
  <a href="https://pypi.org/project/allye-mcp/"><img src="https://img.shields.io/pypi/v/allye-mcp.svg" alt="PyPI"></a>
  <a href="https://www.python.org/"><img src="https://img.shields.io/badge/python-3.10%2B-blue.svg" alt="Python"></a>
  <a href="./LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License"></a>
  <a href="https://codecov.io/gh/allye-devshire/allye-mcp-py"><img src="https://codecov.io/gh/allye-devshire/allye-mcp-py/branch/main/graph/badge.svg" alt="codecov"></a>
</p>

<p align="center">
  <a href="#quick-start">Quick Start</a> •
  <a href="#installation">Installation</a> •
  <a href="#configuration">Configuration</a> •
  <a href="#project-structure">Structure</a> •
  <a href="./TESTING.md">Testing</a>
</p>

---

## Overview

Allye MCP connects MCP-compatible clients (Claude Code, Cursor, Windsurf, VS Code, etc.) directly to the Allye Cloud APIs. Every tool invocation hits the live backend—no outdated snapshots or hallucinated IDs.

**Available Tools:**
- `knowledge` — Documentation CRUD, work items, modes, skills
- `productivity` — TODO management
- `intelligence` — Memories and smart operations
- `user_config` — User configuration documents
- `initialize` — Personalized setup
- `health` — Backend health check

---

## Quick Start

### Remote server with native OAuth (recommended)

Configure the server name `allye` with the canonical URL:

```text
https://mcp.allye.app/mcp
```

Use the client's native MCP OAuth flow. Do not configure a fixed
`Authorization` header, PAT, legacy client ID, tenant path, or token helper.
The client discovers OAuth from:

```text
https://mcp.allye.app/.well-known/oauth-protected-resource/mcp
```

Examples:

```bash
# Claude Code
claude mcp add --transport http allye https://mcp.allye.app/mcp

# Codex
codex mcp add allye --url https://mcp.allye.app/mcp
codex mcp login allye
```

For OpenCode, define an enabled remote server named `allye` at the same URL,
then run `opencode mcp auth allye`. With Pi and `pi-mcp-adapter`, configure the
same name and URL, then use `/mcp-auth allye` and `/mcp reconnect allye`.
Each installation completes its own browser authorization.

### Local STDIO with an explicit PAT

PAT authentication remains supported only for local STDIO:

```bash
pipx install allye-mcp
ALLYE_TRANSPORT_MODE=stdio allye-mcp --pat <your-token>
```

---

## Requirements

| Mode | Authentication | Client |
|------|----------------|--------|
| Remote HTTP | Native OAuth v2 | OAuth-capable MCP client |
| Local STDIO | Explicit Allye PAT | Any STDIO-capable MCP client |

Python 3.10+ is required when running the server locally.

---

## Installation

### With pipx (recommended)

```bash
pipx install allye-mcp
```

### With pip

```bash
pip install --user allye-mcp
```

### Upgrade

```bash
pipx upgrade allye-mcp
# or
pip install --upgrade allye-mcp
```

---

## Configuration

### STDIO client setup

The PAT belongs only to the local STDIO process:

```json
{
  "mcpServers": {
    "allye": {
      "command": "allye-mcp",
      "args": ["--pat", "your-token"],
      "env": {
        "ALLYE_TRANSPORT_MODE": "stdio"
      }
    }
  }
}
```

`ALLYE_PAT_TOKEN` may replace `--pat` for STDIO. Never attach either credential
to an HTTP MCP request.

<details>
<summary><strong>Environment variables</strong></summary>

| Variable | Purpose | HTTP requirement |
|----------|---------|------------------|
| `ALLYE_API_URL` | Private Allye API listener used for delegated calls | Required |
| `ALLYE_AUTH_SERVER_URL` | OAuth issuer (`https://api.allye.app/oauth/v2` in production) | Required |
| `ALLYE_JWKS_URL` | Issuer JWKS endpoint (`https://api.allye.app/oauth/v2/jwks`) | Required |
| `ALLYE_MCP_RESOURCE` | Public resource/audience (`https://mcp.allye.app/mcp`) | Required |
| `ALLYE_INTERNAL_RESOURCE` | Private API audience (`https://api.allye.app/internal/mcp`) | Required |
| `ALLYE_MCP_CLIENT_ID` | Internal client (`allye-mcp-internal`) | Required |
| `ALLYE_MCP_CLIENT_SECRET` | Internal client secret from the secret manager | Required |
| `ALLYE_ALLOWED_ORIGINS` | JSON array of browser origins (production: `["https://allye.app"]`) | Required |
| `ALLYE_JWKS_CACHE_TTL` | Public-key cache duration in seconds | Default `3600` |
| `ALLYE_TRANSPORT_MODE` | `stdio`, `http`, or `both` | Set to `http` or `both` |
| `ALLYE_HTTP_HOST` | HTTP bind host | Default `127.0.0.1` |
| `ALLYE_HTTP_PORT` | HTTP bind port | Default `3000` |
| `ALLYE_LOG_LEVEL` | `DEBUG`, `INFO`, `WARNING`, `ERROR`, or `CRITICAL` | Default `INFO` |
| `ALLYE_PAT_TOKEN` | Local STDIO credential | Never used for HTTP |

See [`.env.example`](./.env.example) for the complete template.

</details>

<details>
<summary><strong>HTTP resource-server operation</strong></summary>

Supply all required values through the deployment secret/configuration manager,
then start without `--pat`:

```bash
export ALLYE_TRANSPORT_MODE=http
allye-mcp
```

The only MCP endpoint is `POST /mcp`. Protected-resource metadata is available
only at `GET /.well-known/oauth-protected-resource/mcp`; `GET /mcp` is not an
SSE transport. `GET /health` remains a public local diagnostic and grants no
tool access.

HTTP and STDIO can run together:

```bash
export ALLYE_TRANSPORT_MODE=both
export ALLYE_PAT_TOKEN=<stdio-only-token>
allye-mcp
```

The HTTP side still requires the full OAuth configuration and never falls back
to that PAT. Missing startup configuration fails closed. If JWKS, service-token,
grant activity, or required API authorization is unavailable, protected
requests return HTTP 503; the server does not reinterpret the failure as an
invalid user token, open a login flow, or run the tool.

</details>

---

## Project Structure

```
allye-mcp-py/
├── allye_mcp/                  # Main package
│   ├── main.py                 # Entry point (CLI)
│   ├── application/            # Use cases and services
│   ├── domain/                 # Business entities
│   ├── infrastructure/         # External integrations (API, transport)
│   └── interface/              # MCP protocol handlers
├── tests/                      # Test suite
├── docs/                       # Documentation
├── pyproject.toml              # Project configuration
└── .env.example                # Environment template
```

<details>
<summary><strong>allye_mcp/</strong> — Package Structure</summary>

| Directory | Responsibility |
|-----------|----------------|
| `main.py` | CLI entry point, argument parsing, server bootstrap |
| `application/` | Use cases, handlers for each MCP tool |
| `domain/` | Business entities, DTOs, validation |
| `infrastructure/` | API client, HTTP transport, logging |
| `interface/` | MCP protocol implementation, tool registration |

### Architecture

```
MCP Client → interface/ → application/ → infrastructure/ → Allye API
                              ↓
                          domain/
                        (entities)
```

</details>

---

## Tech Stack

| Layer | Technology |
|-------|------------|
| Runtime | Python 3.10+ |
| Validation | Pydantic 2 |
| HTTP | aiohttp, requests |
| Config | pydantic-settings |
| Testing | pytest, pytest-asyncio |
| Linting | flake8, black, mypy |

---

## Development

### Setup

```bash
# Clone and install
git clone <repo>
cd allye-mcp-py
pip install -e .[dev]
```

### Scripts

| Command | Description |
|---------|-------------|
| `pytest` | Run tests |
| `pytest --cov=allye_mcp` | Run with coverage |
| `black allye_mcp/ tests/` | Format code |
| `flake8 allye_mcp/ tests/` | Lint |
| `mypy allye_mcp/` | Type check |

### Commit Convention

Follow [Conventional Commits](https://www.conventionalcommits.org/):

| Prefix | Description | Version Bump |
|--------|-------------|--------------|
| `fix:` | Bug fixes | Patch |
| `feat:` | New features | Minor |
| `BREAKING CHANGE:` | Breaking changes | Major |
| `chore:` | Maintenance | None |
| `docs:` | Documentation | None |

---

## CI/CD

| Platform | Usage |
|----------|-------|
| GitHub Actions | Tests, lint, build on push/PR to main |
| Semantic Release | Auto-version based on commits |
| PyPI | Package distribution |

---

## Troubleshooting

<details>
<summary><code>command not found: allye-mcp</code></summary>

Add scripts directory to PATH:

```bash
# macOS/Linux
export PATH="$PATH:~/.local/bin"

# Windows
# Add %APPDATA%\Python\Python311\Scripts to PATH
```

</details>

<details>
<summary><code>401 Unauthorized</code></summary>

- Remote HTTP: the grant or access token is no longer active. Use the
  harness's native authentication command for the `allye` entry when you
  intentionally want to authorize again. Do not add a static bearer header.
- Local STDIO: check the explicit `--pat` or `ALLYE_PAT_TOKEN` value.

</details>

<details>
<summary><code>503 Service Unavailable</code></summary>

OAuth activity could not be established safely. Keep the existing client
registration and credentials, restore the issuer/JWKS/private API dependency,
and retry. A 503 is not a signal to clear credentials or open a new browser
authorization.

</details>

<details>
<summary>Run HTTP and STDIO simultaneously</summary>

Configure all HTTP OAuth variables, then provide a PAT only for the STDIO side:

```bash
export ALLYE_TRANSPORT_MODE=both
export ALLYE_PAT_TOKEN=<stdio-only-token>
allye-mcp
```

</details>

---

## Security

- Keep the internal MCP client secret in the deployment secret manager.
- Let remote harnesses store OAuth credentials in their native credential store.
- Keep local STDIO PATs out of project configuration and version control.
- Never forward external bearer tokens to the Allye API.
- Revoke only the affected MCP grant when access is no longer needed.
- Use `pipx` to isolate a local STDIO installation from global Python.

---

## Useful Links

| Resource | URL |
|----------|-----|
| PyPI | https://pypi.org/project/allye-mcp/ |
| Allye Cloud | https://allye.devshire.app |
| MCP Protocol | https://modelcontextprotocol.io |
