Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# CLAUDE.md - Harbor Project Reference

## Project Overview

Harbor is a Firefox browser extension with a native Node.js bridge that brings AI models and MCP (Model Context Protocol) tools to web applications. It enables web pages to access AI capabilities via `window.ai` and `window.agent` APIs while maintaining strict origin-based permissions and local-first privacy.

## Tech Stack

- **Extension**: TypeScript, Vite, Web Extensions API (Firefox MV3)
- **Bridge**: Node.js 18+, TypeScript, MCP SDK, better-sqlite3, drizzle-orm
- **LLM Providers**: Ollama, llamafile, custom providers via any-llm-ts submodule
- **Testing**: Vitest
- **Packaging**: esbuild, pkg (binary), macOS PKG installer

## Project Structure

```
harbor/
├── extension/ # Firefox Extension
│ ├── src/
│ │ ├── background.ts # Native messaging, permissions
│ │ ├── sidebar.ts # Main UI (servers, chat, settings)
│ │ ├── directory.ts # Server catalog UI
│ │ └── provider/ # window.ai/window.agent injection
│ └── manifest.json
├── bridge-ts/ # Node.js Native Messaging Bridge
│ └── src/
│ ├── main.ts # Entry point, message loop
│ ├── handlers.ts # 50+ message type dispatch
│ ├── types.ts # Shared interfaces
│ ├── host/ # MCP host, permissions, rate limiting
│ ├── mcp/ # MCP protocol clients
│ ├── llm/ # LLM provider abstraction
│ ├── chat/ # Agent loop, tool routing
│ ├── installer/ # Server installation, Docker
│ ├── catalog/ # Server directory, database
│ └── any-llm-ts/ # Git submodule - unified LLM interface
├── demo/ # Example web pages
├── installer/macos/ # macOS .pkg builder
└── docs/ # Documentation
```

## Build & Run

### Prerequisites
- Node.js 18+, Firefox 109+
- Ollama or llamafile (for LLM)
- Optional: Python 3.9+ with uvx, Docker

### Build from Source

```bash
# Clone with submodules
git clone --recurse-submodules <repo-url>
cd harbor

# Build any-llm-ts submodule first
cd bridge-ts/src/any-llm-ts && npm install && npm run build && cd ../../..

# Build bridge
cd bridge-ts && npm install && npm run build && cd ..

# Build extension
cd extension && npm install && npm run build && cd ..

# Install native messaging manifest (macOS)
./bridge-ts/scripts/install_native_manifest_macos.sh

# Load in Firefox: about:debugging → Load Temporary Add-on → extension/dist/manifest.json
```

### Development

```bash
# Watch mode
cd bridge-ts && npm run dev
cd extension && npm run dev
```

### Testing

```bash
cd bridge-ts && npm test
cd extension && npm test

# With coverage
npm run test:coverage
```

## Key Architectural Concepts

### Message Flow
```
Web Page (window.ai) → Extension (background.ts) → Native Messaging → Bridge (handlers.ts) → MCP/LLM
```

### Permission System
- **Scopes**: `model:prompt`, `model:tools`, `mcp:tools.list`, `mcp:tools.call`, `browser:activeTab.read`
- **Grants**: `ALLOW_ONCE` (10min), `ALLOW_ALWAYS` (persistent), `DENY`
- Stored in browser storage (persistent) and memory (ephemeral)

### Tool Registry
- Tools namespaced as `{serverId}/{toolName}` (e.g., `filesystem/read_file`)
- Automatic registration on MCP server connection

### Server Lifecycle
```
INSTALLING → STOPPED → STARTING → RUNNING ↔ CRASHED (auto-restart 3x)
```

### Data Storage
All data in `~/.harbor/`:
- `harbor.db` - Server configs (SQLite)
- `catalog.db` - Cached catalog
- `secrets/credentials.json` - API keys (mode 600)
- `sessions/*.json` - Chat history

## Common Development Tasks

### Add New Message Type
1. Define in `bridge-ts/src/types.ts`
2. Add handler in `bridge-ts/src/handlers.ts`
3. Add response handling in `extension/src/background.ts`

### Add Curated MCP Server
Edit `bridge-ts/src/directory/curated-servers.ts`

### Add New LLM Provider
1. Create `bridge-ts/src/llm/newprovider.ts` implementing `LLMProvider`
2. Register in `bridge-ts/src/llm/manager.ts`

## Code Conventions

- **Files**: `kebab-case.ts`
- **Classes/Interfaces**: `PascalCase`
- **Functions**: `camelCase`
- **Constants**: `SCREAMING_SNAKE`
- **Commits**: Conventional commits (`feat:`, `fix:`, `docs:`, `test:`, `chore:`)
- **TypeScript**: Strict mode, explicit return types on exports
- **Formatting**: 2-space indent, LF line endings (see `.editorconfig`)

## Key Files

| File | Purpose |
|------|---------|
| `extension/src/background.ts` | Extension entry, native messaging |
| `extension/src/sidebar.ts` | Main UI (3150 lines) |
| `bridge-ts/src/handlers.ts` | Message dispatch (3430 lines) |
| `bridge-ts/src/host/host.ts` | MCP host orchestration |
| `bridge-ts/src/chat/orchestrator.ts` | Agent loop implementation |

## Documentation

- `ARCHITECTURE.md` - System design, security model
- `docs/USER_GUIDE.md` - Installation and usage
- `docs/DEVELOPER_GUIDE.md` - API reference
- `docs/JS_AI_PROVIDER_API.md` - window.ai/window.agent API
- `docs/MCP_HOST.md` - MCP execution internals
- `CONTRIBUTING.md` - Development setup
24 changes: 19 additions & 5 deletions bridge-ts/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions bridge-ts/scripts/install_native_manifest_macos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BRIDGE_DIR="$(dirname "$SCRIPT_DIR")"

# Firefox extension ID - must match manifest.json
EXTENSION_ID="harbor@example.com"
EXTENSION_ID="raffi.krikorian.harbor@gmail.com"

# Manifest name (must match what the extension connects to)
MANIFEST_NAME="harbor_bridge_host"
Expand All @@ -18,13 +18,21 @@ MANIFEST_DIR="$HOME/Library/Application Support/Mozilla/NativeMessagingHosts"
# Path to the bridge executable (node running the compiled JS)
BRIDGE_MAIN="$BRIDGE_DIR/dist/main.js"

# Find node - use full path since Firefox launches with minimal environment
NODE_PATH=$(which node)
if [ -z "$NODE_PATH" ]; then
echo "Error: node not found in PATH"
exit 1
fi

# Create a launcher script that runs the bridge with node
LAUNCHER_SCRIPT="$BRIDGE_DIR/harbor-bridge"

echo "Creating launcher script at $LAUNCHER_SCRIPT..."
echo "Using node at: $NODE_PATH"
cat > "$LAUNCHER_SCRIPT" << EOF
#!/bin/bash
exec node "$BRIDGE_MAIN"
exec "$NODE_PATH" "$BRIDGE_MAIN"
EOF
chmod +x "$LAUNCHER_SCRIPT"

Expand Down
Loading