Skip to main content
Glama
leyriel

Fake Pennylane MCP Server

by leyriel
README.md
# Fake Pennylane MCP Server

An open-source fake server that emulates the Pennylane MCP experience for local development, automated tests, demos, and CI pipelines — without requiring a real Pennylane account or real accounting data.

> Status: working early implementation. The repository now includes a runnable Streamable HTTP MCP server, deterministic fixtures, scenario packs, static bearer auth, and a fake OAuth flow for callback/integration testing.

## Why this project exists

Pennylane MCP is useful, but it is hard to validate an integration when you do not have:

- a real Pennylane account,
- a portfolio of test companies and customers,
- deterministic accounting fixtures,
- a safe environment for demos and CI.

This project solves that gap by providing a predictable, developer-friendly test double for a Pennylane-like MCP server.

## Current capabilities

The current implementation provides:

- a **Streamable HTTP MCP server**,
- deterministic fixture loading from JSON,
- scenario-pack selection,
- optional **static bearer token** authentication,
- a **fake OAuth authorization-code flow** for end-to-end callback testing,
- automated local tests for runtime, scenarios, and auth.

### Exposed tools

- `get_context`
- `get_company`
- `list_companies`
- `list_customers`
- `get_customer`
- `list_customer_invoices`

## Quickstart

### Requirements

- Python 3.11+
- `uv`

### Install dependencies

```bash
cd /home/leyriel/dev/fake-pennylane-mcp-server
uv sync
```

### Run the server

Default fixture dataset, no auth:

```bash
uv run python -m fake_pennylane_mcp_server --host 127.0.0.1 --port 8000
```

With a scenario pack:

```bash
uv run python -m fake_pennylane_mcp_server \
  --host 127.0.0.1 \
  --port 8000 \
  --scenario unpaid_invoice
```

With static bearer auth:

```bash
uv run python -m fake_pennylane_mcp_server \
  --host 127.0.0.1 \
  --port 8000 \
  --auth-mode static_bearer \
  --bearer-token test-secret-token
```

With fake OAuth flow:

```bash
uv run python -m fake_pennylane_mcp_server \
  --host 127.0.0.1 \
  --port 8000 \
  --auth-mode fake_oauth
```

## Run with Docker Compose

Build and start the default server:

```bash
docker compose up --build
```

Run detached:

```bash
docker compose up --build -d
```

Stop it:

```bash
docker compose down
```

Compose now defaults to a more exotic host port (`18080`) to avoid common collisions. If you still need a different host port, override it explicitly:

```bash
PUBLISHED_PORT=28080 docker compose up --build -d
```

Use a scenario pack:

```bash
SCENARIO_NAME=unpaid_invoice docker compose up --build
```

Use static bearer auth:

```bash
AUTH_MODE=static_bearer BEARER_TOKEN=test-secret-token docker compose up --build
```

Compose now defaults to `AUTH_MODE=fake_oauth` for easier browser-based local integration tests. To switch explicitly:

```bash
AUTH_MODE=fake_oauth docker compose up --build
```

By default Compose exposes the server on `http://127.0.0.1:${PUBLISHED_PORT:-18080}/mcp`, publishes OAuth metadata on the same host port, and mounts `./fixtures` read-only into the container.

## Scenario packs

The default fixture file currently exposes these scenario packs:

- `default`
- `customer_found`
- `customer_not_found`
- `unpaid_invoice`
- `multi_company`

Each scenario narrows the deterministic dataset to a stable business situation.

## Authentication modes

### `none`
No Authorization header is required.

### `static_bearer`
Requires a bearer token on MCP HTTP requests.

Example header:

```text
Authorization: Bearer ***
```

### `fake_oauth`
Exposes a minimal fake OAuth authorization server alongside the MCP resource server.

Included endpoints:

- `/.well-known/oauth-authorization-server`
- `/.well-known/oauth-protected-resource/mcp`
- `/authorize`
- `/token`

Current fake client contract:

- `client_id`: `fake-public-client`
- token endpoint auth method: `none`
- redirect URIs:
  - `http://127.0.0.1:9999/callback`
  - `http://localhost:9999/callback`
  - `http://127.0.0.1:3800/api/settings/pennylane/callback`
  - `http://localhost:3800/api/settings/pennylane/callback`
- scope: `mcp:access`

Useful Compose defaults for MissionGuard local OAuth validation:

- `AUTH_MODE=fake_oauth`
- `ISSUER_URL=http://127.0.0.1:${PUBLISHED_PORT:-18080}`
- `RESOURCE_SERVER_URL=http://127.0.0.1:${PUBLISHED_PORT:-18080}/mcp`

This flow is meant for local integration tests and fake callback simulations — not as a production OAuth server.

## Compatibility and status tracking

See:

- `docs/compatibility-matrix.md`
- `docs/README.md`

## Example clients

See:

- `examples/basic_client.py`
- `examples/bearer_client.py`
- `examples/README.md`

## Running tests

```bash
uv run pytest -q
```

## Development workflow

Typical local loop:

1. update fixtures / scenarios / auth behavior,
2. run `uv run pytest -q`,
3. run one of the example clients against a local server,
4. commit the change.

## Project structure

```text
.
├── docs/
├── examples/
├── fixtures/
├── src/
├── tests/
├── CONTRIBUTING.md
├── Dockerfile
├── docker-compose.yml
├── LICENSE
├── README.md
└── pyproject.toml
```

## Roadmap

### Bootstrap

- [x] Create Plane project
- [x] Create public GitHub repository
- [x] Publish initial detailed README
- [x] Prepare repo structure for implementation handoff

### Implementation milestones

- [x] Implement fake Streamable HTTP MCP runtime
- [x] Add deterministic fixture loading
- [x] Add fake customer / invoice queries
- [x] Add scenario packs
- [x] Add auth modes for integration testing
- [x] Add runnable example clients
- [x] Add fake OAuth flow for end-to-end callback testing
- [x] Publish a compatibility matrix
- [x] Add Docker and Docker Compose local runtime

### Next useful milestones

- [ ] add GitHub Actions CI once a token with `workflow` scope is available
- [ ] enrich compatibility with more Pennylane-like tools
- [ ] add more domain fixture packs
- [ ] add standalone OAuth example client
- [ ] document additional integration recipes

## Open-source direction

The intent is for this project to be genuinely useful to a broader audience, not just a one-off internal spike.

That means we optimize for:

- clarity of documentation,
- ease of local setup,
- stable deterministic fixtures,
- transparent scope,
- simple contribution paths.

## Project origin

This repository was spun out from work around a MissionGuard integration effort, where a Pennylane MCP replacement strategy needed a realistic test environment without depending on a real Pennylane tenant.

## License

MIT.

## Disclaimer

This project is **not affiliated with or endorsed by Pennylane**.
It is an independent open-source testing utility intended to emulate part of a Pennylane-like MCP integration surface for development and testing purposes only.