What Are Monorepos? A 101 Guide for JavaScript, Python, Next.js, AI Agents, and MCP (2026)
Monorepos explained: one repo, many packages. Compare tools (npm, pnpm, Bun, Turborepo, Nx, Poetry, uv), with Next.js, Python, agent skills, and MCP server examples plus authoritative references.
Most production software is not one folder and one package.json. It is a marketing site, a dashboard, shared database types, a Python MCP server, and a folder of agent skills—all shipping together. A monorepo (monolithic repository) keeps those pieces in one Git tree so you can change an API and every consumer in the same commit.
This guide is Monorepos 101: definitions, when they help, how JavaScript, Next.js, and Python setups differ, and how AI agents and MCP servers fit the layout—with references and copy-paste examples. Tooling stats and version notes below were checked against primary sources (Turborepo, Nx, MCP TypeScript SDK, monorepo.tools) in May–June 2026.
Key takeaways
Monorepo ≠ monolith. You still deploy separate apps; you version them together.
Workspaces (npm, pnpm, Yarn, Bun) wire local packages with workspace:* dependencies—foundation of JS monorepos.
Orchestrators (Turborepo, Nx, Bazel) add caching and task graphs; optional until CI time hurts.
Next.js apps are usually apps/web-style packages that depend on shared packages/* libraries.
Python often lives in parallel (externals/, services/) with Poetry or uv workspaces—not merged into npm.
Agent skills are repo-level instructions (); are separate processes agents call at runtime.
Sometimes tools, docs, infra, and externals (vendored or git-subtree code)
The term comes from contrast with polyrepo (many repos, one product per repo). Google’s public engineering literature and tools like Bazel popularized monorepos at extreme scale; startups and mid-size teams often use npm/pnpm workspaces + Turborepo for a lighter version of the same idea.
Nx’s monorepo.tools defines a monorepo as one repository with multiple distinct projects and well-defined relationships—not mere “code colocation” and not a single undivided giant app. That framing matters in 2026 because repo boundaries are walls for AI agents as much as for humans: polyrepos force you to re-explain APIs and types at every edge; monorepos let agents read implementations directly.
Citation is not deployment. Shipping apps/web to Vercel does not mean you ship externals/python-substack to the same host—you still build and deploy each artifact; the monorepo only guarantees they evolve together in Git.
Monorepo vs polyrepo
Dimension
Monorepo
Polyrepo
Cross-package refactor
One PR updates API + all callers
Coordinate releases across repos
Shared code
packages/* imported via workspace protocol
Publish to npm/PyPI or duplicate
CI
One pipeline; affected-only with Nx/Turbo
Per-repo pipelines
Ownership
Needs CODEOWNERS / path rules
Repo boundary = team boundary
Clone size
Grows with everything
Smaller per repo
Access control
Finer-grained in Git hosting
Repo-level permissions
Good monorepo fit: one product family, shared types, agents + web + MCP maintained by overlapping teams.
Good polyrepo fit: legally separate products, wildly different release cycles, or open-source libraries with external contributors who should not see internal apps.
Anatomy of a modern product monorepo
A layout common in Next.js + agents + MCP products (including directories like explainx.ai that combine web apps, Prisma packages, and Python tooling):
apps/* — things users or customers run in production browsers/services.
packages/* — libraries apps depend on; never import apps/web from packages/db.
externals/* — Python, forks, or submodules that use their own pyproject.toml.
.agents/skills — not npm packages; consumed by Cursor, Claude Code, or compatible harnesses (agent skills guide).
JavaScript and TypeScript monorepos
npm / pnpm / Yarn / Bun workspaces
All major JS package managers support workspaces: multiple package.json files linked locally.
Root package.json (Bun-style workspaces):
json
{"name":"my-product","private":true,"workspaces":["apps/*","packages/*"],"scripts":{"dev":"bun run --filter '@my-product/web' dev","build":"bun run --filter '*' build"}}
One schema change in packages/db → regenerate client → TypeScript errors in every app that imports db in the same PR. That is the monorepo superpower.
Turborepo and Nx (orchestration)
When build, test, and lint run across ten packages, naive npm run build --workspaces rebuilds everything every time.
Turborepo (vercel/turborepo) adds a task graph, local/remote caching, and content-addressed hashing so unchanged packages skip work. Public signals as of May 2026: roughly 30,000+ GitHub stars, homepage at turborepo.dev, active 2.9.x release line. Practitioner write-ups report large CI time reductions after migrating to Turborepo 2.x—treat as directional, not a guarantee on your graph.
Important: Turborepo 2.x renamed the top-level config key from pipeline to tasks. Older tutorials still show pipeline; that fails schema validation on 2.x. Migrate with:
The ^build microsyntax means “run build in dependencies first”—exactly what you want when apps/web imports packages/db.
Nx (nrwl/nx) adds affected detection, generators, and optional Nx Cloud CI distribution. As of May 2026: roughly 28,500 GitHub stars, ~9M weekly npm downloads on the core nx package (per npm registry stats), latest stable around 22.7.x. Nx’s own positioning in 2026 explicitly includes AI agents—caching and affected runs matter when agents trigger full-repo builds more often.
Open-source agent workflow repos such as gstack (covered in our gstack deep dive) use Nx-style layouts for slash commands and shared tooling.
When to add orchestration: CI exceeds ~10 minutes, or developers routinely skip building packages they did not touch (and break main). Many small teams stay on pnpm -r alone until pain appears—that is a valid 2026 pattern (pnpm production monorepo notes).
Next.js in a monorepo
Next.js does not require a monorepo, but monorepos are the default for teams running multiple Next apps (marketing + logged-in product) plus shared UI.
Next.js can compile dependencies from local packages or node_modules via transpilePackages (stable since v13.0.0 per Next.js docs).
You strictly need it when:
The workspace package is resolved as a symlinked node_modules boundary and Next would otherwise skip transpilation
The package imports CSS or assets that trigger “Global CSS cannot be imported from within node_modules”
You export raw TS/TSX without a separate build step and the bundler treats the package as external
You may not need it when the bundler resolves the package like local source (common in some Turborepo/Nx setups with project references)—a Next.js discussion (#93542) documents both behaviors.
Turbopack note (2026): monorepo + subpath exports in internal packages can behave differently under Turbopack vs Webpack; if internal packages fail to resolve, check Next.js issue #85315 and fall back to --webpack until your graph is supported.
Environment variables
Each app keeps its own .env.local. Database URLs usually live in apps/web and apps/learn, while packages/db reads process.env.DATABASE_URL at runtime—document which app owns which secret in README or AGENTS.md so coding agents do not guess wrong.
Caveats from official uv docs: workspaces share one uv.lock and an intersected requires-python across members—if one service needs Python 3.14 and another caps at 3.11, use path dependencies with separate envs instead of one workspace. Large open-source examples (e.g. Apache Airflow’s multi-package tree, discussed on Talk Python #540) use uv workspaces for per-package uv sync so import boundaries match declared dependencies.
Path filters (paths: ['apps/**', 'packages/**']) avoid running Python jobs on doc-only commits.
AI agents in a monorepo
Agent skills are not npm packages. They are markdown instruction bundles (often SKILL.md + references) that Claude Code, Cursor, or compatible harnesses load when a task matches.
Same repo the agent edits gets the same conventions (blog frontmatter, Prisma patterns, MCP import scripts).
One PR can update a skill and the code it describes.
Discovery:npx skills add and registries (explainx.ai skills) distribute skills; in-repo copies stay pinned to your branch.
Example skill frontmatter agents read:
markdown
---
name: blog-writing
description: Write SEO/GEO-optimized blog posts for explainx.ai following established patterns.
---# Blog Writing Skill
Content location: `/apps/web/content/blog/`
Agent harness note: Terminal agents (OpenClaw, Claude Code, etc.) benefit from monorepos because search and grep span the whole product—but large repos need .cursorignore / context rules so agents do not load node_modules or generated Prisma clients into context. Tools like incremental indexes (CocoIndex) target exactly this “living monorepo” problem.
2026 industry framing:monorepo.tools and Nx’s “polyrepo tax” narrative emphasize atomic cross-project PRs, single-source shared libraries, and agent-visible context across frontend, backend, and tools—without claiming every company should merge all repos tomorrow. Synthetic monorepos (dependency graphs across separate Git repos) are an incremental option when full consolidation is blocked.
MCP servers in a monorepo
Model Context Protocol (MCP) standardizes how hosts (Cursor, Claude Desktop, IDE extensions) call tools and read resources from external processes.
MCP servers in a monorepo are usually:
TypeScript — official SDK in packages/mcp-foo or tools/mcp-foo
Python — FastMCP or the Python SDK in externals/* or services/*
Not embedded in Next.js — they run as stdio/SSE processes configured in the IDE
Developer runs changeset and describes bump (patch / minor / major).
Changesets accumulate under .changeset/.
On release, CI versions packages and updates changelogs.
bash
# One-time setup at repo root
bun add -D @changesets/cli
bun changeset init
markdown
<!-- .changeset/brave-lions.md -->
---"db": patch
---
Fix connection pool defaults for serverless builds.
Lerna and Beachball solve similar problems; greenfield repos in 2026 usually pick Changesets + pnpm or Changesets + Turborepo.
Python: publish packages/py-common to PyPI independently with uv publish or Poetry poetry publish; tag releases in Git matching the monorepo tag py-common-v0.4.0 if multiple artifacts leave one repo.
Deploying from a monorepo
Each app is still one deployable unit. Platforms read a root directory or filter:
Platform
Pattern
Vercel
Project root = apps/web; install from repo root with cd ../.. && bun install
Docker
Multi-stage build: copy root lockfile → install → bun run --filter web build
Fly.io / Railway
dockerfile per app or monorepo-aware buildpack
Illustrative Dockerfile (Next app in workspace):
dockerfile
FROM oven/bun:1 AS deps
WORKDIR /app
COPY package.json bun.lock ./
COPY apps/web/package.json apps/web/
COPY packages/db/package.json packages/db/
RUN bun install --frozen-lockfile
FROM deps AS builder
COPY . .
RUN bun run --filter web build
FROM node:22-slim AS runner
WORKDIR /app
COPY --from=builder /app/apps/web/.next/standalone ./
ENV NODE_ENV=production
CMD ["node", "server.js"]
MCP and Python services deploy as separate containers or run on developer machines—do not co-locate with Next unless you operate a process supervisor (usually unnecessary).
Goal: Add “export skills to CSV” — touches DB schema, web API, docs, agent skill, optional MCP tool.
Step
Location
Action
1
packages/db/prisma/schema.prisma
Add SkillExport model
2
packages/db
prisma migrate dev + commit generated client
3
apps/web/app/api/...
Route using prisma from db
4
apps/web/content/blog/...
Document feature (BLUF + FAQ)
5
.agents/skills/...
Update skill: “run export via API route X”
6
packages/mcp-skills (optional)
MCP tool wrapping same API for Claude
One pull request, one CI run, reviewers see the full vertical slice—impossible to coordinate that cleanly across five polyrepos without release choreography.
A monorepo is one Git repository holding many related packages—Next.js apps, shared TypeScript libraries, Python MCP servers, and agent skills—so you can refactor across boundaries in a single change. JavaScript workspaces link apps/* to packages/*; Python typically uses separate pyproject.toml files or uv workspaces under externals/ or services/. Next.js consumes shared packages via workspace:* and transpilePackages. Agents read repo-level skills; MCP runs as separate stdio servers configured in the IDE, not inside the Next bundle.
Pick workspaces first, add Turborepo or Nx when CI hurts, and keep dependency arrows pointing inward toward shared packages. That layout is how modern AI-native product teams ship web, data, tools, and coding agents from one place.
Tool versions (Next.js 16.x, Prisma 6.x, FastMCP 3.x, MCP SDK v1.29.x, Turborepo 2.9.x, Nx 22.7.x) and star/download figures were checked against upstream docs and registries in May–June 2026; re-verify before production upgrades.