ANTIGRAVITY LABJP
Articles/Agents & Manager
Agents & Manager/2026-03-23Advanced

NotebookLM × Antigravity — Building Context-Aware Codebase Knowledge Agents

Learn how to leverage NotebookLM's 8x context window as a codebase knowledge layer and connect it to Antigravity agents via MCP bridge for context-aware code generation, refactoring, and architectural decisions.

NotebookLM2Antigravity321AI Agent5MCP17CodebaseKnowledge BaseAgent Orchestration

Setup and context

Every developer working with large codebases faces a fundamental constraint: context window limitations. You can't fit millions of lines of code into a single prompt, and even if you could, models trained on historical data struggle to understand your project's current architectural decisions and design philosophy.

NotebookLM—Google's sophisticated AI knowledge management platform—solves this problem by serving as a specialized knowledge layer. With its 8x expanded context window, NotebookLM can simultaneously analyze multiple design documents, architecture specifications, and code references. When connected to Antigravity agents through Model Context Protocol (MCP), this becomes a powerhouse for context-aware code generation, intelligent refactoring, and architectural decision support.

This article walks you through building a production-ready system where NotebookLM acts as a persistent knowledge repository and Antigravity's manager surface orchestrates multiple specialized agents that query this knowledge base to make informed decisions about your codebase.


Understanding NotebookLM's 8x Context Window

How the Extended Context Works

Claude's standard API provides approximately 100K tokens of context. NotebookLM extends this to roughly 800K token equivalents through intelligent mechanisms:

1. Document-Level Indexing & Dynamic Retrieval

Rather than treating your codebase as a monolithic blob, NotebookLM:

  • Segments your documentation into logical units (files, modules, architectural layers)
  • Creates semantic indexes for each segment
  • Dynamically retrieves only the most relevant portions for each query
  • Maintains reasoning chains across multiple document boundaries

2. Hybrid Search Architecture

NotebookLM combines:

  • Lexical search: Fast keyword matching for file names, function signatures
  • Semantic search: Vector similarity to find "calls to getUserProfile()" or "components using Suspense pattern"
  • Graph-based reasoning: Understanding that FileA.ts imports FileB.ts, which exports a type that impacts FileC.ts

3. Incremental Context Assembly

Queries are processed in layers:

  1. Query analysis → identify relevant concepts
  2. Retrieve primary documents (high relevance)
  3. Fetch secondary context (related patterns, similar implementations)
  4. Build final context window only from assembled pieces
  5. Return answer within the expanded window

This is dramatically more efficient than pre-loading everything.

Optimal Document Structure for Codebases

To maximize NotebookLM's effectiveness, structure your knowledge base as:

NotebookLM Codebase Knowledge Base
├─ Architecture & Design
│  ├─ ARCHITECTURE.md (system design, module relationships)
│  ├─ DATA_MODEL.md (schemas, types, entities)
│  ├─ MODULE_DEPENDENCY_GRAPH.md (visual/text format)
│  └─ DESIGN_PATTERNS.md (adopted patterns & their rationales)
├─ Codebase Reference
│  ├─ packages/*/README.md (package purposes & exports)
│  ├─ src/*/PUBLIC_API.md (public interfaces only)
│  └─ [Optional] key entry points (.tsx, .ts)
├─ Operational Knowledge
│  ├─ DEPLOYMENT.md
│  ├─ TESTING_STRATEGY.md
│  ├─ PERFORMANCE_TUNING.md
│  └─ TROUBLESHOOTING.md
└─ Decision Records
   ├─ ADR/ (Architecture Decision Records)
   └─ RFC/ (implemented proposals)

Key principle: Prioritize why over what. Include design rationales, trade-offs documented in ADRs, and public interfaces—but not every internal implementation detail. NotebookLM excels at understanding intent and reasoning, not memorizing 10,000 lines of implementation.


The MCP Bridge: Connecting NotebookLM to Antigravity Agents

NotebookLM as an MCP Server

Model Context Protocol (MCP) standardizes how LLM applications access external tools, databases, and knowledge systems. By implementing NotebookLM as an MCP server, Antigravity agents can query the knowledge base as if it's part of their extended memory—instantly and reliably.

Here's a production-grade implementation:

// mcp/notebooklm-server.ts
import Anthropic from "@anthropic-ai/sdk";
 
interface NotebookQuery {
  notebook_id: string;
  query: string;
  max_results?: number;
  context_depth?: "shallow" | "deep"; // shallow: direct matches, deep: multi-hop relationships
  focus_area?: string; // e.g., "performance", "security", "migration"
}
 
interface ContextResult {
  source: string; // filename or section
  relevance: number; // 0.0 to 1.0
  excerpt: string; // relevant text, ~300-500 chars
  related_items: string[]; // other sections this connects to
  confidence: number; // how certain we are this answers the query
}
 
class NotebookLMMCPServer {
  private client: Anthropic;
  private notebookCache: Map<string, string> = new Map();
  private queryCache: Map<string, ContextResult[]> = new Map();
 
  async queryNotebook(req: NotebookQuery): Promise<ContextResult[]> {
    // Check cache first (queries are expensive)
    const cacheKey = `${req.notebook_id}:${req.query}:${req.context_depth}`;
    if (this.queryCache.has(cacheKey)) {
      return this.queryCache.get(cacheKey)!;
    }
 
    // Fetch the notebook content (pre-exported or via API)
    const notebookContent = await this.fetchNotebook(req.notebook_id);
 
    // Use Claude to search within the notebook
    const message = await this.client.messages.create({
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 2000,
      system: `You are a specialized knowledge retrieval system for a codebase.
Given a notebook of codebase documentation, extract the most relevant information.
 
For each result, provide:
- source: exact section name or filename
- relevance: confidence 0.0-1.0
- excerpt: the key relevant text
- related_items: ["Section A", "Section B"] - what else is connected
- confidence: how certain this directly answers the user's question
 
Return results as a valid JSON array.`,
      messages: [
        {
          role: "user",
          content: `Documentation:\n\n${notebookContent}\n\nQuestion: ${req.query}\n\nFind the ${req.max_results || 5} most relevant results.${req.focus_area ? ` Focus on ${req.focus_area}.` : ""}`,
        },
      ],
    });
 
    const results = this.parseResults(message.content[0]);
 
    // Cache for future requests
    this.queryCache.set(cacheKey, results);
    if (this.queryCache.size > 1000) {
      // LRU eviction if cache grows too large
      const firstKey = this.queryCache.keys().next().value;
      this.queryCache.delete(firstKey);
    }
 
    return results;
  }
 
  private async fetchNotebook(notebookId: string): Promise<string> {
    if (this.notebookCache.has(notebookId)) {
      return this.notebookCache.get(notebookId)!;
    }
 
    // TODO: Integrate with NotebookLM API or load from storage
    // For now, assume pre-exported markdown
    const content = await this.loadFromFileSystem(notebookId);
    this.notebookCache.set(notebookId, content);
    return content;
  }
 
  private parseResults(content: any): ContextResult[] {
    try {
      const parsed = JSON.parse(content.text);
      return Array.isArray(parsed) ? parsed : [];
    } catch {
      console.error("Failed to parse NotebookLM response");
      return [];
    }
  }
}
 
export default NotebookLMMCPServer;

Agent Query Workflow

Here's how an Antigravity agent uses this MCP server in practice:

// In Antigravity IDE
agent.registerMCPTool("notebook_query", {
  description: "Query the NotebookLM codebase knowledge base",
  inputSchema: {
    type: "object",
    properties: {
      query: {
        type: "string",
        description: "Natural language question about codebase architecture, patterns, or decisions",
      },
      context_depth: {
        type: "string",
        enum: ["shallow", "deep"],
        default: "shallow",
      },
      focus_area: {
        type: "string",
        enum: ["performance", "security", "reliability", "maintainability"],
      },
    },
    required: ["query"],
  },
});
 
// Agent uses it autonomously
agent.think(
  "I need to refactor getUserData(). Let me check what the current design expects."
);
 
// Internally executes:
const context = await agent.callMCPTool("notebook_query", {
  query: "getUserData function: responsibilities, dependencies, error handling patterns",
  context_depth: "deep",
  focus_area: "reliability",
});
 
// Returns with specific knowledge about this function's role
console.log(context);
// [
//   {
//     source: "ARCHITECTURE.md",
//     relevance: 0.95,
//     excerpt: "Data retrieval layer handles async operations with Suspense...",
//     related_items: ["ERROR_HANDLING.md", "TESTING_STRATEGY.md"]
//   },
//   ...
// ]

Multi-Agent Orchestration with Manager Surface

Specialized Agents, Unified Knowledge Base

NotebookLM enables agents to specialize by domain while maintaining architectural consistency:

AgentKnowledge FocusSpecialization
Architecture AgentARCHITECTURE.md, ADRs, MODULE_DEPENDENCY_GRAPH.mdSystem-wide impact analysis, migration strategy, refactoring scope
Code Quality AgentCODE_PATTERNS.md, TESTING_STRATEGY.md, STYLE_GUIDE.mdCode generation, refactoring, test coverage, patterns
Performance AgentPERFORMANCE_TUNING.md, METRICS.md, DEPENDENCY_GRAPH.mdBottleneck identification, optimization suggestions, impact measurement
Security AgentSECURITY.md, COMPLIANCE.md, THREAT_MODEL.mdVulnerability detection, secure pattern enforcement
Documentation AgentAll documentationAuto-generating guides, examples, keeping docs in sync

Antigravity's Manager Surface orchestrates these agents:

// Antigravity Manager Surface
class CodebaseCoordinationManager {
  async refactorComponentWithFullAnalysis(
    componentPath: string,
    refactoringGoal: string
  ): Promise<ComprehensiveRefactoringPlan> {
    // 1. Architecture Agent: What are the blast radius & dependencies?
    const architectureAnalysis = await this.architectureAgent.analyze({
      query: `What components depend on ${componentPath}?
              What would break if we change this?
              Which systems would this affect?`,
      focus_area: "reliability",
    });
 
    // 2. Code Quality Agent: What's the best approach?
    const implementationStrategy = await this.codeQualityAgent.generate({
      query: `${refactoringGoal} for ${componentPath}.
              Follow our project patterns.
              Include tests.`,
      context: architectureAnalysis,
    });
 
    // 3. Performance Agent: What's the impact?
    const performanceReview = await this.performanceAgent.evaluate({
      proposed_code: implementationStrategy,
      baseline_metrics: await this.getMetrics(componentPath),
      query: "Will this improve or degrade performance? Bundle size?",
    });
 
    // 4. Security Agent: Any risks?
    const securityReview = await this.securityAgent.review({
      changes: implementationStrategy,
      affected_systems: architectureAnalysis.dependencies,
    });
 
    // 5. Documentation Agent: Update guides
    const docUpdates = await this.documentationAgent.generate({
      changes: implementationStrategy,
      reason: refactoringGoal,
    });
 
    // Synthesize into a comprehensive plan
    return {
      architecture: architectureAnalysis,
      implementation: implementationStrategy,
      performance: performanceReview,
      security: securityReview,
      documentation: docUpdates,
      executionOrder: this.determineExecutionOrder([
        architectureAnalysis,
        implementationStrategy,
        performanceReview,
        securityReview,
      ]),
      rollbackStrategy: await this.planRollback(
        architectureAnalysis.dependencies
      ),
      approvalCheckpoints: this.identifyHumanCheckpoints(
        performanceReview,
        securityReview
      ),
    };
  }
}

Knowledge Consistency Across Agents

Because all agents query the same NotebookLM knowledge base:

  • They work from consistent premises
  • Contradictions are rare
  • The "source of truth" is automatically maintained
  • When documentation updates, all agents see the new information immediately

Production Scenario: Large-Scale Refactoring

The Challenge

You have a 500+ file TypeScript/React codebase using Redux for state management. Your goal: migrate to Context API + Zustand with zero breaking changes and measurable performance improvements.

This is complex because:

  • Redux selectors are used across 200+ components
  • Actions have 50+ test files depending on them
  • Removing Redux means changes across the entire application
  • Performance predictions must be accurate (false promises break trust)

Stage 1: Preparing the Knowledge Base

Feed NotebookLM documents covering:

# State Management Architecture
 
## Current System (Redux)
- Redux store location: `src/store/redux/`
- Selectors: `src/selectors/*` (48 files, 3000+ lines)
- Actions: `src/actions/*` (32 files, 2500+ lines)
- Test coverage: 89%
 
**Pain points:**
- Boilerplate: 1 action = 5-8 files (action, type, reducer, selector)
- DevTools coupling: Redux DevTools middleware required
- Bundle size: Redux + middleware = 45KB (gzipped)
 
## Target System (Zustand)
- Store files: `src/stores/zustand/*.ts`
- Philosophy: Minimal boilerplate, functional API
- Bundle size estimate: 12KB (gzipped)
 
## Migration Strategy
 
### Phase 1: Global State (Week 1-2)
- User authentication store
- Theme/preferences store
- Global UI state (modals, notifications)
 
### Phase 2: Domain Stores (Week 3-4)
- Product management (currently Redux ProductSlice)
- Cart management (Redux CartSlice)
- Orders (Redux OrdersSlice)
 
### Phase 3: Local State Conversion (Week 5)
- Complex local Redux stores → Zustand
- Cleanup: Remove Redux library
 
## Pattern Equivalency Guide
 
### Redux Selector → Zustand Pattern
```typescript
// Redux: const user = useSelector(selectUser)
// Zustand: const user = useUserStore(s => s.user)

Redux Action Dispatch → Zustand Action

// Redux: dispatch(setUser(data))
// Zustand: useUserStore.setState({user: data})

Risk Assessment

  • Estimated effort: 160 developer-hours
  • Risk level: Medium (breaking changes in 5% of test suite estimated)
  • Rollback window: 2 weeks
  • Success metrics: bundle -60%, performance +15%, test coverage maintained

NotebookLM now understands:
- The current Redux structure in detail
- The target Zustand design
- Exact equivalencies between patterns
- Risk and effort estimates
- Success metrics

### Stage 2: Agent Analysis & Planning

```typescript
// User triggers refactoring in Antigravity IDE
> Refactor: Redux → Zustand (Full Migration)

// Manager Surface coordinates:

const refactoringPlan = await manager.planRefactoring({
  currentArchitecture: "Redux + middleware",
  targetArchitecture: "Zustand stores",
  scope: "application-wide",
});

// Agent 1: Architecture
const impactAnalysis = await archAgent.query({
  query: "Show Redux store tree, identify which components use Redux, dependencies between stores",
});
// Returns: 48 selectors, 72 actions, 200 connected components

// Agent 2: Code Quality
const patterns = await codeQualityAgent.query({
  query: "Generate Zustand equivalents for these Redux patterns",
  provided: impactAnalysis,
});
// Returns: Zustand store templates, hook equivalencies

// Agent 3: Performance
const perfPredictions = await performanceAgent.analyze({
  proposed_stores: patterns,
  baseline: { bundleSize: 45, ttfb: 2100, lcpScore: 0.74 },
  query: "Predict performance impact of Zustand migration",
});
// Returns: -60% bundle, +200ms faster TTI, +15% LCP improvement

// Agent 4: Testing
const testStrategy = await testAgent.plan({
  current_tests: await getTestCoverage("Redux"),
  target_architecture: "Zustand",
  query: "Update test suite for Zustand. Maintain 89% coverage",
});
// Returns: 50+ new test patterns, deprecation scripts

// Agent 5: Documentation
const devGuide = await docAgent.generate({
  query: "Write migration guide for developers",
  context: [impactAnalysis, patterns, perfPredictions],
});

Stage 3: Incremental Execution with Learning

// Execute migration in manageable chunks with continuous learning
 
for (const phase of refactoringPlan.phases) {
  console.log(`Executing: ${phase.name}`);
 
  // Generate and review code
  const generatedCode = await agent.generate(phase);
  const review = await human_review(generatedCode); // Developer reviews in IDE
 
  // Run tests
  const testResults = await runTests(phase.affected_files);
 
  if (!testResults.allPassed) {
    // **Learn from failures**: Update NotebookLM with lessons
    await notebookLM.addNote({
      title: `Migration Issue: ${testResults.failedTest}`,
      root_cause: await agent.analyze(testResults.failure),
      resolution: "...",
      lesson_for_next_phase: "When handling store subscriptions, remember...",
    });
 
    // **Adjust next phase** based on what we learned
    const nextPhaseAdjustments = await archAgent.replan({
      lessons_learned: notebookLM.getRecentNotes(),
      original_plan: refactoringPlan.phases[i + 1],
    });
 
    refactoringPlan.phases[i + 1] = nextPhaseAdjustments;
  }
 
  // Measure actual vs predicted metrics
  const actualMetrics = await measure({
    bundleSize: "after compilation",
    performance: "run lighthouse",
    coverage: "run coverage report",
  });
 
  const prediction = perfPredictions[phase];
  const accuracy = calculateAccuracy(prediction, actualMetrics);
 
  // Store metrics for next refactoring's accuracy improvement
  await notebookLM.recordMetrics({
    phase: phase.name,
    predicted: prediction,
    actual: actualMetrics,
    accuracy: accuracy,
  });
}

This creates a learning loop: each phase refines the knowledge base, improving future agent decisions.


Keeping NotebookLM Synchronized

Continuous Document Updates

NotebookLM is only as valuable as its documentation is current. Implement automated synchronization:

// Daily sync pipeline
class NotebookSyncManager {
  async runDailySync() {
    // 1. Detect what changed in the codebase
    const gitDiff = await git.getDiff("HEAD~1", "HEAD", [
      "src/",
      "docs/",
      ".adr-dir/",
    ]);
 
    // 2. Classify changes (some require doc updates, others don't)
    for (const change of gitDiff) {
      const classification = await classifyImpact(change);
 
      if (classification === "ARCHITECTURE_CHANGE") {
        // e.g., new module, major refactor
        const updatedArchDoc = await autoGenerate.architecture(change);
        await updateFile("docs/ARCHITECTURE.md", updatedArchDoc);
      } else if (classification === "API_CHANGE") {
        // e.g., public interface modified
        const updatedAPI = await autoGenerate.publicAPI(change);
        await updateFile("docs/PUBLIC_API.md", updatedAPI);
      } else if (classification === "PATTERN_CHANGE") {
        // e.g., new pattern adopted
        const updatedPatterns = await autoGenerate.patterns(change);
        await updateFile("docs/CODE_PATTERNS.md", updatedPatterns);
      }
    }
 
    // 3. Push updated docs to NotebookLM
    await notebookLM.sync({
      updateSections: Object.keys(changedDocs),
      retainHistory: true, // Keep version history
    });
 
    console.log(`Synced ${Object.keys(changedDocs).length} doc sections`);
  }
}
 
// Run daily (e.g., via GitHub Actions)
schedule.daily("03:00 UTC", () => new NotebookSyncManager().runDailySync());

Leveraging NotebookLM Enterprise API

For deeper integration, Google's NotebookLM Enterprise API offers:

import { NotebookLMClient } from "@google-cloud/notebooklm";
 
const nlm = new NotebookLMClient({ apiKey: process.env.NOTEBOOKLM_API_KEY });
 
// 1. Generate Podcast: Turn architecture docs into audio guides
const podcast = await nlm.generatePodcast({
  notebookId: "antigravity-codebase",
  sourceDocuments: ["ARCHITECTURE.md", "DESIGN_PATTERNS.md"],
  audienceLevel: "advanced_engineer",
  style: "discussion", // 2 speakers discussing the topic
  duration: 30, // minutes
});
 
// Use for onboarding new team members
await onboarding.addResource({
  type: "audio",
  url: podcast.publicUrl,
  title: "Architecture Overview (30 min)",
  assignedTo: newEngineer,
});
 
// 2. Manage Notebook Collections: Connect related notebooks
await nlm.createCollection({
  name: "Antigravity Codebase",
  description: "All documentation and knowledge for the Antigravity IDE codebase",
  notebooks: [
    { id: "arch-notebook", role: "source_of_truth" },
    { id: "performance-notebook", role: "derived_analysis" },
    { id: "migration-notebook", role: "execution_plan" },
    { id: "decision-records", role: "historical" },
  ],
  enableCrossReferences: true,
});
 
// Now queries can reference across notebooks
const query = await nlm.query({
  collectionId: "Antigravity Codebase",
  question: "How does the migration to Zustand impact performance according to ARCHITECTURE.md and PERFORMANCE_TUNING.md?",
});

Conclusion

Connecting NotebookLM to Antigravity agents via MCP creates a knowledge-driven development system:

  • 8x expanded context: Understand projects with unprecedented depth
  • Knowledge consistency: All agents work from the same source of truth
  • Safe complexity: Large refactorings become systematic, testable, reversible
  • Accumulated wisdom: ADRs, migration learnings, and performance data automatically enrich the knowledge base

Your codebase becomes a living system that learns from each refactoring, each decision, each migration. This is the foundation for truly intelligent AI-assisted development.

Ready to build this? Start with Antigravity agent memory patterns and the MCP server implementation guide.

Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Agents & Manager2026-06-15
Designing Schema Evolution So Sub-Agent Handoffs Never Break
Put a typed contract at the boundary where a downstream agent receives an upstream agent's output, and learn how to evolve that schema without breaking existing flows — with validation code, a migration sequence, and the production symptoms to watch for.
Agents & Manager2026-07-10
Tracing Why an Agent Wrote That Line Six Months Ago — Commit Granularity and Provenance Trailers
When an agent packs 14 files and 800 lines into a single commit, git blame tells you nothing six months later. Here is how I split commits at intent boundaries, recorded provenance as machine-readable Git trailers, and built a one-command path from a blamed line back to the design decision behind it.
Agents & Manager2026-07-10
When Your Agent's Files Vanish Into .gitignore — A Pre-Commit Detection Gate
When an agent writes files that match .gitignore, the diff review looks perfect but nothing lands in the commit. Here is a gate script that catches ignored build output before you push, plus the tuning it needs.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →