FireBall1725/pcexpress-mcp-server
by FireBall1725
README.md
# PC Express MCP Server
> **Unofficial** Model Context Protocol (MCP) server for PC Express / Loblaws grocery shopping
Control your grocery cart with AI! This MCP server enables LLMs (like Claude) to search past orders, find products, and manage your shopping cart across all Loblaws-owned grocery banners.
[](https://www.gnu.org/licenses/agpl-3.0)
[](https://www.python.org/downloads/)
> **How the auth was cracked:** the login sits behind Akamai bot detection, so the OAuth flow was reverse-engineered out of Loblaw's Android app. If you want the emulator-and-mitmproxy story behind `pcid_config.py`, read the write-up: [Reverse-engineering PC Express](https://fireball1725.ca/articles/reverse-engineering-pc-express-oauth-secret).
## β οΈ Important Disclaimers
- **Unofficial**: This project is NOT affiliated with, endorsed by, or supported by Loblaws Companies Limited, PC Express, or any related entities
- **Use at Your Own Risk**: This uses an undocumented API that may change without notice
- **No Warranty**: See [LICENSE](LICENSE) for details
- **Educational Purpose**: This project demonstrates API reverse engineering and MCP server development
## π Supported Grocery Banners
Works with all PC Express enabled stores:
- **Zehrs** - Ontario-based grocery chain
- **Loblaws** - Main flagship banner
- **No Frills** - Discount grocery banner
- **Real Canadian Superstore** - Western Canada supermarket
- **Your Independent Grocer** - Franchise stores
- **T&T Supermarket** - Asian specialty supermarket
## β¨ Features
### MCP Tools (6)
1. **search_past_orders** - Find items from your order history
2. **get_order_items** - Get detailed product list from specific orders
3. **search_products** - Search the product catalog
4. **add_to_cart** - Add products to your shopping cart
5. **remove_from_cart** - Remove items from cart
6. **view_cart** - See current cart contents
### Integration Ready
- β
**Home Assistant** - Voice-controlled grocery shopping
- β
**Claude Desktop** - Chat with your grocery list
- β
**Custom Clients** - Any MCP-compatible application
### Voice Assistant Examples
```
π€ "Add ice cream to my grocery cart"
π€ "What did I order last week?"
π€ "Search for organic bananas"
π€ "What's in my cart?"
```
## π Quick Start
### Prerequisites
- Python 3.10 or higher
- Active account with a PC Express enabled banner
- pip or pip3
### Installation
1. **Clone the repository**
```bash
git clone https://github.com/FireBall1725/pcexpress-mcp-server.git
cd pcexpress-mcp-server
```
2. **Install dependencies**
```bash
pip install -r requirements.txt
```
3. **Configure credentials**
```bash
cp .env.example .env
# Edit .env with your credentials (see Configuration section)
```
4. **Test the connection**
```bash
python test_api.py
```
5. **Run the server**
```bash
python pcexpress_mcp_server.py
```
## βοΈ Configuration
### Getting Your Credentials
Authentication uses the PC Express app's OAuth flow. You sign in once in a real browser to
get a refresh token; after that the server mints access tokens on its own and never needs
another manual login. See [AUTH_NOTES.md](AUTH_NOTES.md) for how it works, and
[DEPLOYMENT.md](DEPLOYMENT.md) for running it locally, in Docker, Home Assistant, or Kubernetes.
**Easiest:** run the setup wizard; it logs you in and writes the config for wherever you run it:
```bash
python setup.py
```
The manual steps below are the same thing, done by hand.
#### One-time login
```bash
python login_pcid.py
```
It opens your browser to the PC ID sign-in. After you log in, the page tries to open a
`com.loblaw.pcx://...` link and shows an error; that is expected. Copy that full address
from the address bar and paste it back into the script. It prints your `PCEXPRESS_REFRESH_TOKEN`.
The app client secret is baked into the code, so you don't supply it.
#### Automated login (optional, no paste)
If you'd rather not paste anything, `login_pcid_auto.py` drives a browser for you:
```bash
pip install -r requirements-auto.txt && python -m playwright install chromium
PCEXPRESS_CLIENT_SECRET=... PCEXPRESS_EMAIL=you@example.com PCEXPRESS_PASSWORD=... \
python login_pcid_auto.py
```
It logs in and prints the same two values. It runs a **headed** browser (Akamai blocks
headless), so run it on a machine with a display, or under `xvfb-run` on a headless box.
Accounts with 2FA can't be automated; use the manual `login_pcid.py` for those. Either way,
the browser is used only for this one-time step; the server never runs a browser.
Your cart id and customer id are read from your profile at runtime, so you don't set them.
### Environment Variables
```bash
# Refresh token from the one-time login above (server rotates/persists it after first use)
PCEXPRESS_REFRESH_TOKEN=your_refresh_token_here
# Writable dir for the rotated token + cached access token; must persist across restarts
PCEXPRESS_STATE_DIR=~/.pcexpress-mcp
# Banner: zehrs, loblaws, nofrills, superstore, independent, tandt
PCEXPRESS_BANNER=zehrs
# Store ID (4-digit code)
PCEXPRESS_STORE_ID=your_store_id_here
# Optional: cart id is auto-discovered; set only to force a specific cart.
# PCEXPRESS_CART_ID=your_cart_id_here
# Optional: the app client secret is baked in; set only to override it.
# PCEXPRESS_CLIENT_SECRET=your_client_secret_here
```
## π Platform Integration
### Home Assistant
```yaml
# configuration.yaml
mcp:
servers:
- name: pcexpress
command: python3
args:
- /path/to/pcexpress_mcp_server.py
env:
PCEXPRESS_REFRESH_TOKEN: "your_refresh_token"
PCEXPRESS_STATE_DIR: "/config/pcexpress-mcp"
PCEXPRESS_STORE_ID: "your_store"
PCEXPRESS_BANNER: "zehrs"
```
### Claude Desktop
Edit your Claude Desktop config:
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"pcexpress": {
"command": "python3",
"args": ["/path/to/pcexpress_mcp_server.py"],
"env": {
"PCEXPRESS_REFRESH_TOKEN": "your_refresh_token",
"PCEXPRESS_STATE_DIR": "~/.pcexpress-mcp",
"PCEXPRESS_STORE_ID": "your_store",
"PCEXPRESS_BANNER": "zehrs"
}
}
}
}
```
Restart Claude Desktop and start chatting about groceries!
### Custom MCP Client
```python
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
server_params = StdioServerParameters(
command="python3",
args=["pcexpress_mcp_server.py"],
env={
"PCEXPRESS_REFRESH_TOKEN": "your_refresh_token",
# ... other env vars
},
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# List available tools
tools = await session.list_tools()
# Call a tool
result = await session.call_tool("search_products", {"query": "ice cream"})
```
## π Documentation
- **[QUICKSTART.md](QUICKSTART.md)** - 5-minute setup guide
- **[SETUP.md](SETUP.md)** - Detailed configuration and login
- **[DEPLOYMENT.md](DEPLOYMENT.md)** - Local, Docker, Home Assistant, and Kubernetes
- **[AUTH_NOTES.md](AUTH_NOTES.md)** - How the PC id auth flow works
- **[API_REFERENCE.md](API_REFERENCE.md)** - The pcx-bff endpoints, params, and responses
- **[BANNERS.md](BANNERS.md)** - Multi-banner usage guide
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - Technical architecture details
- **[CONTRIBUTING.md](CONTRIBUTING.md)** - How to contribute
- **[Reverse-engineering PC Express](https://fireball1725.ca/articles/reverse-engineering-pc-express-oauth-secret)** - The write-up on getting the OAuth secret out of the app
## β οΈ Known Limitations
Access-token expiry and cart-id changes used to be the big ones. Both are solved now:
the server refreshes its own tokens (rotating and persisting the single-use refresh
token), and it reads the cart id from your profile at runtime. What's left:
### 1. Product search reliability
**Problem**: Search goes through the banner website's Next.js route, which sits behind
Akamai, so it can be flakier than the authenticated pcx-bff calls.
**Impact**: A search can occasionally fail even when the rest of the API is fine.
**Workaround**: Retry, or use past orders to find product codes.
**Status**: π The app's token-authenticated `/products/search` route is a candidate replacement.
### 2. No official API
**Problem**: This is a reverse-engineered, undocumented API. Loblaw can change it, or
rotate the app client secret in a future release and break every copy at once.
**Impact**: Things can break without notice.
**Status**: β οΈ Inherent to the approach.
### 3. Store-specific inventory
**Problem**: Product availability and pricing vary by store and banner.
**Workaround**: Set `PCEXPRESS_STORE_ID` to the store you want.
**Status**: β
Expected behaviour.
## π Troubleshooting
### Auth error / "re-run login_pcid.py"
**Cause**: The refresh token was consumed or revoked. Refresh tokens are single-use, so a
shared token chain (two instances on one token) or a long idle period can break it.
**Fix**: Run `python login_pcid.py` (or `setup.py`) and update `PCEXPRESS_REFRESH_TOKEN`.
A normal expired access token is refreshed automatically and needs nothing from you.
### "404 Not Found"
**Cause**: No orders or cart at that banner, or the wrong `PCEXPRESS_BANNER` code. Cart and
customer ids are read from your profile, so a stale id isn't the cause.
**Fix**: Check `PCEXPRESS_BANNER` is the banner you actually shop.
### "No products found"
**Cause**: Store doesn't carry product or wrong store ID
**Fix**: Verify `PCEXPRESS_STORE_ID` is correct
### Tests fail
**Cause**: Missing dependencies or invalid credentials
**Fix**:
```bash
pip install -r requirements.txt
# if the smoke test reports an auth error, your refresh token expired:
python login_pcid.py
```
See full troubleshooting guide in [SETUP.md](SETUP.md)
## πΊοΈ Roadmap
### Done
- [x] Automatic token refresh (no manual token copying)
- [x] Cart id and customer id auto-discovery
### Open
- [ ] Token-authenticated product search (replace the website route)
- [ ] Store id auto-discovery from `homeStore`
- [ ] Better error messages
### Medium Priority
- [ ] Shopping list management
- [ ] PC Optimum points integration
- [ ] Checkout/order placement
- [ ] Product details (images, nutrition, etc.)
- [ ] Price tracking
### Low Priority
- [ ] Multiple banner support in single instance
- [ ] Recipe suggestions based on cart
- [ ] Price comparison across banners
See [Issues](https://github.com/FireBall1725/pcexpress-mcp-server/issues) for full list.
## π€ Contributing
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
### Quick Contribution Guide
1. Fork the repo
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Test thoroughly (`python test_api.py`)
5. Commit (`git commit -m 'Add amazing feature'`)
6. Push (`git push origin feature/amazing-feature`)
7. Open a Pull Request
## π£ Support
Questions, updates, and works in progress: [FireBall Codes on Discord](https://discord.gg/QpV82CFfVD).
If this saved you some time, you can [buy me a sushi roll](https://ko-fi.com/fireball1725).
## π License
This project is licensed under the GNU Affero General Public License v3.0 - see the [LICENSE](LICENSE) file for details.
## π Acknowledgments
- Thanks to the MCP protocol team at Anthropic
- Inspired by the need for better grocery shopping automation
- Built with reverse-engineering and determination
## βοΈ Legal
This project is provided for **educational and personal use only**.
- Not affiliated with Loblaws Companies Limited
- Uses undocumented API - use at your own risk
- May violate Terms of Service - check before using
- No warranty or guarantee of functionality
- Author not responsible for any consequences of use
By using this software, you agree to take full responsibility for your usage.
## π§ Contact
- **Issues**: [GitHub Issues](https://github.com/FireBall1725/pcexpress-mcp-server/issues)
---
**Made with β€οΈ for better grocery shopping**
*If this project saved you time, consider starring β the repo!*
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessWithin a week