239 students. 20 active cohorts. 74 MCP tools. One thin proxy. Case-manager triage dropped from ~5 hours/week to ~10 minutes/day. Employer job matching went from 2–3 staff to 1. Cohort management went from ~10 hours/week to ~1 hour/week.
The sponsor
EDDR Student Management System — an end-to-end platform for NCCER-accredited trade schools. Live at portal.eddrcorp.com (admin) and kiosk.eddrcorp.com (attendance kiosk PWA).
Stack: ASP.NET Core 8 API + Angular 20 admin app + Angular 20 PWA kiosk + MySQL.
Staff roles enforced by the system: Admin, Registrar, CaseManager, Counselor, Instructor, LeadInstructor, ProgramManager, Director, Executive, ComplianceOfficer, RecruitmentSpecialist, FrontDesk, Accounting, FinancialAidOfficer, ReadOnly, Auditor.
Pain before the agent:
- Case managers spending mornings manually triaging who was absent and why
- Instructor roll-call overrides happening on paper, lost before reaching the registrar
- Employer job openings sitting in email until the next Monday staff meeting
- NCCER credential progress checked by hand against module completion records
What changed
What got installed
- EDDR API — already running on a VPS. The MCP endpoint was added at
/mcp in the same ASP.NET Core process. No separate MCP server, no new infrastructure.
- qbit agent runtime — Docker VM instances on the sponsor's on-premise server. The agent runs on hardware the school owns, inside their building.
- qbit.me cloud dashboard — hosted by qbit.me. Manages the agent remotely: updates, monitoring, session history, team collaboration. The dashboard is the control plane; the runtime stays on the sponsor's premises.
The 74 tools
| Domain | Tools | What the agent does |
| Students | 6 | Search by name/NCCER/Worknet, read profile, create/update with role check |
| Enrollments | 8 | List cohort enrollments, create/update, read module progress |
| Attendance | 5 | Read per-session records, post entries, update (audit-logged) |
| Shortfalls | 6 | List, summarize, acknowledge, excuse, bulk-acknowledge |
| Cohorts & sessions | 14 | CRUD cohorts + sessions, duplicate cohort for new term, bulk-replace instructors |
| Courses & modules | 11 | Course/module CRUD, duplicate course, list cohorts per course |
| Module grading | 5 | Written/digital/performance scores, instructor-scoped |
| Jobs & employers | 10 | Job opportunity CRUD, status, archive, matching-students endpoint |
| Dashboard & reports | 6 | Stats, activity feed, enrollment/cohort reports, filters |
| Reference data | 3 | Trades, student categories |
Architecture
The MCP layer is a thin proxy (~200 lines). The agent does not get a second permission system — it gets the same one the staff already use.
Sponsor staff (browser / phone / kiosk)
│ qbit agent (Docker VM, on-premise server)
│ JWT (existing) │ MCP JSON-RPC + X-Api-Key
▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ EDDR Student System API (ASP.NET Core, VPS) │
│ /api/* ◄── existing REST controllers │
│ RBAC, instructor scoping, validation, │
│ audit log, notification side-effects │
│ /mcp ◄── MCP server (Streamable HTTP) │
│ McpProxyHelper forwards X-Api-Key to /api/* │
│ SHA-256 hashed, per-user, role-scoped │
└──────────────────────────────────────────────────────────────┘
│
▼
MySQL (student records, audit log, MCP key hashes)
┌─────────────────────────────────┐
│ qbit.me cloud (hosted by qbit) │
│ Remote management, monitoring │
└─────────────────────────────────┘
│ manages agent remotely
▼
qbit agent (on-premise)
Three real workflows
Workflow A — Attendance shortfall triage
Agent drafts, case manager approves.
Each morning at 07:30, the agent calls the shortfall tools, drafts a follow-up note per unacknowledged absence using the existing outcome taxonomy, and posts each as a draft. The case manager reviews drafts and clicks Approve → agent commits via AcknowledgeShortfall or ExcuseShortfall.
Result: case-manager triage went from ~5 hours/week to ~10 minutes/day reviewing automated reports.
Workflow B — Employer job matching
Agent suggests, counselor approves.
The agent watches for new Active job opportunities on a 15-minute cron, calls GetMatchingStudents, and drafts a per-student message with credential readiness status. The counselor reviews and approves → agent sends via EDDR's existing notification service.
Result: job matching shifted from 2–3 staff spending 8–12 hours/week to 1 staff spending ~1 hour/week on attestation and review.
Workflow C — Cohort close-out and credential readiness
Agent reads, registrar reviews.
A weekly cron produces a one-page readiness brief per cohort ending within 14 days: who's completed all modules, who has outstanding grades, who has unacknowledged shortfalls that would block NCCER registry submission. The brief lands in the registrar's qbit.me inbox with deep links into the admin app.
Result: cohort and course management went from ~10 hours/week across the term to ~1 hour/week reviewing pre-built briefs.
The trust model
Everything here is backed by code that already exists.
- Authentication: Every MCP call carries
X-Api-Key. Keys are SHA-256 hashed at rest, per-user, rotatable from the profile page.
- Authorization: Each key carries a
Roles string. The existing [Authorize(Roles = RoleSets.X)] on every controller enforces them — exactly as it does for JWT-authenticated staff.
- Audit: Every mutation goes through the same controller path that staff use. The existing
AuditLogs table records it with the key's UserId as the actor. No separate "agent audit log."
- Revocation: Keys can be deactivated or rotated instantly. No agent retains access after a key is deactivated.
- Scope-per-key: Issue a read-only key to an auditor, a Counselor-scoped key to the counselor's agent, and a full Admin key to the registrar's agent. No superuser-by-default.
- Data residency: EDDR API and MySQL on the sponsor's VPS. qbit agent on the sponsor's on-premise server. qbit.me cloud manages remotely but does not mirror student records.
Agent access to student records is scoped, audited, and revocable — by the same controls that govern staff access.
Outcomes
| Metric | Before | After | Reduction |
| Case-manager absence triage | ~5 hours/week | ~10 min/day (~50 min/week) | ~6x |
| Employer job matching | 2–3 staff, 8–12 hours/week | 1 staff, ~1 hour/week | ~8–12x time, 2–3x headcount |
| Cohort + course management | ~10 hours/week over 10–12 week term | ~1 hour/week | ~10x |
| Students managed through the agent | 0 | 239 students, 20 active cohorts | — |
"Cohort implementation and student registrations used to be tedious and time-consuming — getting from unstructured data sources into the system. Now, I'm simply reviewing and approving imported data."
— Jordan Phillips, EDDR Executive Assistant
Why this is a qbit.me story
The agent is not a cloud feature EDDR charges for. It is a qbit agent running on the sponsor's own server. The sponsor owns the runtime, the keys, and the audit trail. qbit.me manages it remotely.
EDDR did not rewrite its business logic to support agents. It added a ~200-line proxy. Every other vertical SaaS vendor with a REST API could do the same in roughly the same effort.
The same on-premise server that runs the agent for EDDR can run agents for the sponsor's accounting tool, their LMS, their CRM — each as a separate MCP server, each with its own scoped key, all under one attested runtime. EDDR is the first tenant, not the only one.
EDDR is the lighthouse. The pattern is the product.
Call to action
For trade schools: See EDDR at portal.eddrcorp.com or contact EDDR directly.
For vertical-SaaS founders: Make your product agent-native — add a thin MCP proxy to your existing API and your customers get a qbit agent on hardware they own.