LP Internal AI MCP Server
by danbanned
README.md
# LP Internal AI — V1
An internal AI intelligence layer for Launchpad that lets team members query Claude with live organizational data — student records, program outcomes, certifications, competency scores, finances, donations, and communications.
---
## Table of Contents
- [System Architecture](#system-architecture)
- [Tech Stack](#tech-stack)
- [Folder Structure](#folder-structure)
- [Workspace Packages](#workspace-packages)
- [Getting Started](#getting-started)
- [Onboarding Resources](#onboarding-resources)
- [Package Management](#package-management)
- [MCP Tools](#mcp-tools)
- [Connectors](#connectors)
- [Current Status](#current-status)
---
## System Architecture
Four logical layers: **data sources → connectors → storage → MCP server + Claude**, with the HQ dashboard running alongside.
```
┌─────────────────────────────────────────────────────────────────────┐
│ DATA SOURCES │
│ │
│ Google Sheets Google Drive BigQuery GiveButter Aplos │
│ (students, (docs, notes, (BI data) (donations) (accounting)│
│ outcomes, student info) │
│ attendance, Slack Roam │
│ finances) (channels) (chat) │
│ Notion │
│ (meeting transcripts) │
└───────────────────────┬─────────────────────────────────────────────┘
│ scheduled syncs (AWS EventBridge)
│ manual: pnpm sync:<name>
▼
┌─────────────────────────────────────────────────────────────────────┐
│ CONNECTORS (9) │
│ │
│ Each connector runs sync() → calls runSync() → writes to │
│ sync_runs table. Errors are captured; HQ dashboard shows status. │
│ │
│ google-sheets google-drive bigquery givebutter aplos │
│ slack roam notion │
└───────────────────────┬─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ STORAGE │
│ │
│ Postgres 16 (AWS RDS / local Docker) │
│ ├── Structured tables (Prisma ORM) │
│ │ students, staff, entity_aliases │
│ │ student_phase_outcomes, student_certifications │
│ │ attendance_records, enrollment_snapshots │
│ │ finance_snapshots │
│ │ donor_contacts, donor_gifts, donor_pipeline, donor_grants │
│ │ student_employment, student_postsecondary │
│ │ sync_runs, usage_logs, aws_resource_jobs │
│ │ mcp_users, oauth_clients, tool_permissions │
│ └── Vector store (pgvector extension) │
│ document_chunks ← OpenAI text-embedding-3-large │
│ (Google Drive docs + Slack + Roam → 1536-dim embeddings) │
│ │
│ Entity Resolution: entity_aliases table deduplicates the same │
│ person across all sources using exact + fuzzy (pg_trgm) matching. │
└───────────────────────┬─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ MCP SERVER (16 tools) │
│ │
│ Exposes structured Prisma queries + pgvector semantic search │
│ as Model Context Protocol tools. │
│ │
│ Transport A: stdio → Claude Desktop (local) │
│ Transport B: Streamable HTTP → AWS ECS Fargate behind ALB (production) │
│ │
│ All tool calls are logged to usage_logs for adoption tracking. │
│ All tools return structured { error: { code, message } } on fail. │
└───────────────────────┬─────────────────────────────────────────────┘
│ MCP protocol
▼
┌─────────────────────────────────────────────────────────────────────┐
│ CLAUDE (Anthropic) │
│ │
│ Receives tool definitions + calls tools at query time. │
│ Composes answers from structured data + semantic search results. │
│ Team members interact via Claude Desktop or any MCP-capable client.│
└─────────────────────────────────────────────────────────────────────┘
┌──────────────────────┐
│ HQ DASHBOARD │
│ (Next.js 14) │
│ │
│ / freshness │
│ /sync sync runs │
│ /tools tool logs │
│ │
│ Auth: NextAuth v5 │
│ Google OAuth │
│ @launchpadphilly.org│
└──────────────────────┘
```
At query time, Claude calls MCP tools → the server runs Prisma queries or pgvector similarity search → results are returned and synthesized into a natural-language answer. At sync time, EventBridge (or `pnpm sync:<name>`) triggers a connector → it upserts rows via Prisma and, for document sources, generates embeddings via OpenAI → every run writes a result to `sync_runs`.
---
## Tech Stack
### Application layer
| Concern | Technology | Notes |
|---|---|---|
| Language | TypeScript 5 (strict) | `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes` |
| Monorepo | pnpm workspaces | 17 workspace packages |
| HQ Dashboard | Next.js 14 (App Router) | Standalone Docker image |
| Auth | NextAuth v5 (Auth.js) | Google OAuth, domain-gated to `@launchpadphilly.org` |
| MCP Server | `@modelcontextprotocol/sdk` | stdio + Streamable HTTP transports |
| Embeddings | OpenAI `text-embedding-3-large` | 1536 dimensions, batch + retry helpers |
| ORM | Prisma | All queries; `$queryRaw` only for pgvector + `percentile_cont` |
| Database | Postgres 16 + pgvector + pg_trgm | Local Docker; AWS RDS in production |
| Validation | Zod | All external data and env vars |
| Tests | Vitest | Unit + live-DB integration + spawned MCP server integration |
| CI | GitHub Actions | pgvector service container; full suite on every push |
### Infrastructure & tooling
| Concern | Technology |
|---|---|
| App hosting | AWS ECS Fargate behind ALB |
| Scheduling | AWS EventBridge |
| Secrets | AWS Secrets Manager (prod) / `.env` (local) |
| Monitoring | Sentry |
| BI dashboards | Metabase (self-hosted on AWS) |
| Workflow automation | n8n (self-hosted on AWS) |
| Data warehouse | Athena over S3 |
| Ingestion tooling | Airbyte (self-hosted; future non-Sheets sources) |
---
## Folder Structure
```
lp-internal-ai-v1/
├── apps/
│ ├── hq/ # Next.js 14 HQ dashboard
│ │ ├── app/
│ │ │ ├── page.tsx # / — data freshness overview
│ │ │ ├── dashboard/page.tsx # /dashboard — analytic dashboard
│ │ │ ├── sync/page.tsx # /sync — connector sync run history
│ │ │ ├── tools/page.tsx # /tools — MCP tool call log
│ │ │ ├── admin/page.tsx # /admin — MCP OAuth + tool permissions
│ │ │ ├── aws-jobs/[id]/ # /aws-jobs/:id — resource job details
│ │ │ └── api/health/route.ts # GET /api/health (unauthenticated)
│ │ ├── auth.ts # NextAuth v5 config + Google provider
│ │ ├── middleware.ts # Route guard
│ │ └── Dockerfile
│ ├── mcp-server/ # MCP server — 16 tools
│ │ ├── src/
│ │ │ ├── index.ts # Entry: stdio (Claude Desktop)
│ │ │ ├── serve-http.ts # Entry: Streamable HTTP (ECS Fargate)
│ │ │ ├── make-server.ts # Tool registration
│ │ │ ├── usage-log.ts # Logs every tool call
│ │ │ └── tools/ # One file per MCP tool (16 files)
│ │ └── Dockerfile
│ ├── aws-mcp-server/ # AWS resource management MCP server
│ │ └── Dockerfile
│ └── sync/ # One-off Fargate task runner for scheduled syncs
│ └── Dockerfile
├── packages/
│ ├── db/ # @lp-ai/lib-db — Prisma client, schema, seed
│ ├── config/ # @lp-ai/lib-config — typed env loader
│ └── embedding/ # @lp-ai/lib-embedding — OpenAI batch helpers
├── connectors/ # One package per source (9 total)
│ ├── google-sheets/ # ✅ Live — 12 sheet syncs, 26K+ records
│ ├── givebutter/ # ✅ Live — REST client, donors/gifts/pipeline
│ ├── aplos/ # ✅ Live — RSA auth, 16K+ records
│ ├── notion/ # ✅ Live — meeting transcript sync + embeddings
│ └── google-drive|bigquery|slack|roam/ # skeletons
├── infra/
│ ├── postgres-init/ # SQL: CREATE EXTENSION pgvector, pg_trgm
│ └── iam/ # AWS IAM policy templates
├── docs/
│ ├── architecture.md # Detailed system overview
│ ├── database-schema.md # All 30 Prisma models with columns + indexes
│ ├── mcp-server-spec.md # All 16 tool definitions (input/output schemas)
│ ├── entity-resolution.md # Cross-source deduplication strategy
│ ├── setup/ # Phase-by-phase AWS setup guides (00–22)
│ ├── runbooks/ # local-dev.md, credentials-checklist.md, aws-permissions.md
│ ├── decisions/ # Architecture Decision Records
│ └── data-sources/ # Per-connector specs
├── docker-compose.yml # Local Postgres 16 + pgvector
├── vitest.config.ts
├── tsconfig.base.json # Shared TS config (ES2022, NodeNext, strict)
├── pnpm-workspace.yaml # Workspace roots: apps/*, packages/*, connectors/*
├── package.json # Root scripts: db:*, sync:*, build, test
└── .env.example # Authoritative inventory of all env variables
```
---
## Workspace Packages
pnpm resolves `workspace:*` references to local source at install time — no publishing required. Build order follows this graph:
```
@lp-ai/lib-config (no internal deps)
@lp-ai/lib-db (no internal deps)
@lp-ai/lib-embedding (no internal deps)
│
├── @lp-ai/connector-google-sheets → lib-config, lib-db
├── @lp-ai/connector-bigquery → lib-config, lib-db
├── @lp-ai/connector-givebutter → lib-config, lib-db
├── @lp-ai/connector-aplos → lib-config, lib-db
├── @lp-ai/connector-google-drive → lib-config, lib-db, lib-embedding
├── @lp-ai/connector-slack → lib-config, lib-db, lib-embedding
├── @lp-ai/connector-roam → lib-config, lib-db, lib-embedding
├── @lp-ai/connector-notion → lib-config, lib-db, lib-embedding
├── @lp-ai/hq → lib-config, lib-db
├── @lp-ai/mcp-server → lib-config, lib-db, lib-embedding
└── @lp-ai/aws-mcp-server → lib-config, lib-db
```
---
## Getting Started
> Full walkthrough: [docs/runbooks/local-dev.md](docs/runbooks/local-dev.md) — Credential requirements: [docs/runbooks/credentials-checklist.md](docs/runbooks/credentials-checklist.md)
**Prerequisites:** Node ≥ 20, pnpm ≥ 9, Docker Desktop running.
```bash
pnpm install # all 17 packages
cp .env.example .env # fill in values you have
pnpm db:up # start Postgres + pgvector
pnpm db:generate # generate Prisma client
pnpm --filter @lp-ai/lib-db push --skip-generate # apply schema
pnpm db:seed # 3 students, donors, finance, certs
pnpm -r typecheck && pnpm test # verify: 42 tests
pnpm --filter @lp-ai/hq dev # HQ → http://localhost:3000
pnpm --filter @lp-ai/mcp-server build
pnpm --filter @lp-ai/mcp-server start # MCP stdio (Claude Desktop)
pnpm --filter @lp-ai/mcp-server start:http # MCP HTTP → http://localhost:8080
```
Only `DATABASE_URL` is required to boot — it defaults to the local Docker Postgres in `.env.example`. All API keys are optional at load time and validated at first use.
---
## Onboarding Resources
- New contributor checklist: [docs/reference/new-developer-playbook.md](docs/reference/new-developer-playbook.md)
- Connector maturity and verification map: [docs/reference/connector-capability-matrix.md](docs/reference/connector-capability-matrix.md)
- Skills workflow reference: [HOW-SKILLS-WORK.md](HOW-SKILLS-WORK.md)
---
## Package Management
All commands run from the **repository root**. Never use `npm` or `yarn`.
### Installing dependencies
```bash
# Add to a specific workspace
pnpm add <package> --filter @lp-ai/<name>
pnpm add -D <package> --filter @lp-ai/<name>
# Add to root (shared tooling only: vitest, eslint, prettier, typescript, prisma CLI)
pnpm add -D -w <package>
```
### Updating dependencies
```bash
pnpm update -r --interactive # interactive update across all packages
pnpm update -r <package> # update one package everywhere
pnpm update --filter @lp-ai/<name> # update all deps in one workspace
```
After updating, commit all changed `package.json` files and `pnpm-lock.yaml` together.
### Removing dependencies
```bash
pnpm remove <package> --filter @lp-ai/<name>
```
### Adding a new internal dependency
Add the `workspace:*` reference manually in the consuming package's `package.json`, then run `pnpm install`:
```jsonc
// e.g. connectors/givebutter/package.json
{
"dependencies": {
"@lp-ai/lib-config": "workspace:*",
"@lp-ai/lib-db": "workspace:*"
}
}
```
### After editing the Prisma schema
```bash
pnpm db:generate # regenerate Prisma client
pnpm --filter @lp-ai/lib-db push --skip-generate # local dev (no migration file)
pnpm db:migrate # production/staging (tracked migration)
```
Use `push` for local iteration. Use `migrate` for any change that must be tracked and replayed on the production RDS instance.
---
## MCP Tools
All tools return structured JSON. Errors use `{ error: { code, message } }`.
| Tool | Description |
|---|---|
| `get_student_info` | Student profile + aliases from Google Drive seed doc |
| `get_entity_brief` | Composite: profile + phase progression + certs + recent mentions + donor history |
| `get_finance_brief` | Composite: budget summary + actuals + grants + giving pipeline |
| `query_students` | Population stats + filtered student lists |
| `query_outcomes` | Phase progression (Foundations → 101 → Lightspeed → LiftOff) |
| `query_enrollment` | Enrollment stats by phase, school, cohort, race |
| `query_certifications` | PCEP exam results and scores |
| `query_competency` | Per-student Beacon competency scores |
| `query_attendance` | Unified attendance across three cohort formats |
| `query_employment` | Post-program employment data (employer, wages, hours, exit codes) |
| `query_postsecondary` | College enrollment tracking (National Student Clearinghouse) |
| `query_finances` | Budgets, actuals, forecasts, stipends, CRM giving/pipeline/grants |
| `query_donors` | Building21 Development CRM donor lookup |
| `search_conversations` | Semantic search over Drive docs (Slack/transcripts in V0.2) |
| `search_by_person` | Scoped document search by student or staff name |
| `search_documents` | Raw document chunk search with optional entity filter |
Full input/output schemas: [docs/mcp-server-spec.md](docs/mcp-server-spec.md)
---
## Connectors
| Connector | Source | Destination | Status |
|---|---|---|---|
| `google-sheets` | Launchpad Dashboard + Outcomes sheets (12 spreadsheets) | Postgres | ✅ Live — all 12 syncs ported; 26K+ records |
| `google-drive` | Drive docs folder | Postgres + pgvector | Skeleton — creds available, implementation pending |
| `bigquery` | `lp-internal-ai` BigQuery project | Postgres | Skeleton — creds available, implementation pending |
| `givebutter` | GiveButter donation platform | `donor_contacts`, `donor_gifts`, `donor_pipeline` | ✅ Live — REST client syncing |
| `aplos` | Aplos nonprofit accounting | Postgres (finance snapshots) | ✅ Live — RSA-decryption auth; 16K+ records |
| `notion` | Notion meeting transcripts database | `document_chunks` (pgvector) | ✅ Live — meeting transcripts with embeddings |
| `slack` | Designated Slack channels | pgvector | Skeleton — awaiting `SLACK_BOT_TOKEN` |
| `roam` | Roam chat / messaging | pgvector | Skeleton — awaiting `ROAM_API_KEY` |
Each connector's `sync()` is wrapped by `runSync()`, which writes a success/error row to `sync_runs` on every run — visible in the HQ `/sync` page.
---
## Current Status
| Area | State |
|---|---|
| Typecheck | ✅ All 17 packages pass |
| Tests | ✅ 42 tests, 6 files (Vitest) |
| Local DB | ✅ Postgres 16 + pgvector via Docker Compose |
| MCP tools | ✅ All 16 wired to real Prisma queries |
| HQ dashboard | ✅ Renders against live Postgres; auth gated; analytic dashboard ported |
| Google Sheets connector | ✅ All 12 sheet syncs live; 26K+ records |
| GiveButter connector | ✅ REST client syncing donors, gifts, pipeline |
| Aplos connector | ✅ RSA-decryption auth; 16K+ records |
| Notion connector | ✅ Meeting transcript sync with embeddings |
| OpenAI embeddings | ✅ `text-embedding-3-large` verified and live |
| AWS account | ✅ Account 851725317896, IAM user configured, us-east-1 |
| Remaining connectors | 🟡 Skeletons — google-drive, bigquery, slack, roam |
| AWS production | 🟡 Docker images + ECS task defs built; deployment in progress |
| CI | ✅ GitHub Actions with pgvector service container |
See [docs/runbooks/credentials-checklist.md](docs/runbooks/credentials-checklist.md) for credential requirements. See [docs/setup/README.md](docs/setup/README.md) for the phase-by-phase AWS production setup status.
This server cannot be deployed
Maintenance
ActivityActive
ResponsivenessSyncing