Demo Video: https://drive.google.com/file/d/1QkB8Z0JpacVcc4Xwcv1yWIa2-mzF__-w
Running a local gaming tournament is a logistical headache. Players miss their matches, don't know their bracket position, and organizers spend the whole event chasing people down. Phones are already in everyone's pocket — why not just text them?
Linq Tournament Manager lets players register, check their bracket, and report results entirely over iMessage. No app to download, no website to navigate — just a conversation. An AI agent handles everything: answering questions about who you play next, what station to go to, and recording your result when the match is done. When a tournament fills up, the bracket generates automatically and every player gets an iMessage with their first matchup.
Built on the Linq API for the Linq technical challenge.
Player (iMessage) → Linq webhook → Express backend → LLM agent (OpenRouter)
↕ MCP tools
SQLite database
React frontend ↗
The backend receives iMessage events from Linq, runs them through a tool-calling LLM agent, and sends replies back via the Linq API. The agent's tools are exposed through an MCP server (stdio transport), keeping the tool logic isolated and reusable. A React frontend gives organizers a live bracket view and provides a web registration form as an alternative to texting in.
| Layer | Tech |
|---|---|
| Messaging | Linq API (iMessage) |
| Backend | Node.js + Express + TypeScript |
| Agent | OpenRouter LLM (tool-calling) |
| Tools | MCP server (stdio transport) |
| Database | SQLite (better-sqlite3) |
| Frontend | React + Vite |
- Node.js 20+
- A Linq partner account with an API key and phone number
- An OpenRouter API key
- ngrok (or equivalent) to expose the local webhook to Linq
npm install
cp .env.example .env
# fill in .env with your credentials# Start everything (backend + MCP server + frontend)
npm run devThe database is created and seeded automatically on first run.
To receive live iMessages from Linq, expose port 3001 with ngrok:
ngrok http 3001Register the ngrok HTTPS URL + /webhook as your Linq webhook endpoint. scripts/setup-webhook.sh automates this once your tunnel is running.
Copy .env.example to .env:
| Variable | Description |
|---|---|
LINQ_API_KEY |
Linq partner API key |
LINQ_FROM_NUMBER |
Your Linq phone number (E.164) |
LINQ_WEBHOOK_SECRET |
Webhook signing secret from Linq dashboard |
OPENROUTER_API_KEY |
OpenRouter API key |
LLM_MODEL |
OpenRouter model ID (default: nvidia/nemotron-3-super-120b-a12b:free) |
DB_PATH |
SQLite file path (default: ./db/tournament.db) |
PORT |
Backend port (default: 3001) |
NGROK_AUTHTOKEN |
ngrok auth token for the setup script |
packages/
backend/ Express server — webhook handler, REST API, LLM agent
mcp-server/ MCP tool server — tournament CRUD exposed as LLM tools
frontend/ React bracket viewer and web registration form
db/
schema.sql Database schema
seed.sql Demo tournaments and CPU players
scripts/
setup-webhook.sh Register ngrok URL with Linq
The backend exposes simulator endpoints for demoing without real players:
POST /api/simulate/advance-cpu— auto-advance a CPU-vs-CPU matchPOST /api/simulate/reset— reset the database to seed state
The frontend includes a simulator panel for clicking through these flows.