Fixed 3 schema compatibility bugs found during Round 3 write testing: - create_document: Added id, urlId, teamId, isWelcome, fullWidth, insightsEnabled - create_collection: Added id, maintainerApprovalRequired - shares_create: Added id, allowIndexing, showLastUpdated All write operations now include required NOT NULL columns. Bumped version to 1.3.6. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
334 lines
12 KiB
Markdown
334 lines
12 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project Overview
|
|
|
|
MCP server for direct PostgreSQL access to Outline Wiki database. Follows patterns established by `mcp-desk-crm-sql-v3`.
|
|
|
|
**Version:** 1.3.6
|
|
**Total Tools:** 164 tools across 33 modules
|
|
**Production:** hub.descomplicar.pt (via SSH tunnel)
|
|
|
|
### Architecture
|
|
|
|
```
|
|
┌─────────────────────┐
|
|
│ src/server/ │
|
|
│ (Shared Logic) │
|
|
└──────────┬──────────┘
|
|
│
|
|
┌────────────────┼────────────────┐
|
|
│ │ │
|
|
┌────────▼────────┐ ┌─────▼─────┐ │
|
|
│ index.ts │ │index-http │ │
|
|
│ (stdio) │ │ (HTTP) │ │
|
|
└─────────────────┘ └───────────┘ │
|
|
│ │ │
|
|
└────────────────┴────────────────┘
|
|
│
|
|
┌──────────▼──────────┐
|
|
│ PostgreSQL │
|
|
│ (Outline DB) │
|
|
└─────────────────────┘
|
|
```
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
# Build TypeScript to dist/
|
|
npm run build
|
|
|
|
# Run stdio server (default, for Claude Code)
|
|
npm start
|
|
|
|
# Run HTTP server (for web/remote access)
|
|
npm run start:http
|
|
|
|
# Development with ts-node
|
|
npm run dev
|
|
npm run dev:http
|
|
|
|
# Run tests
|
|
npm test
|
|
```
|
|
|
|
## Transports
|
|
|
|
| Transport | Entry Point | Port | Use Case |
|
|
|-----------|-------------|------|----------|
|
|
| stdio | `index.ts` | N/A | Claude Code local |
|
|
| HTTP | `index-http.ts` | 3200 | Web/remote access |
|
|
|
|
### HTTP Transport Endpoints
|
|
|
|
- `/mcp` - MCP protocol endpoint
|
|
- `/health` - Health check (JSON status)
|
|
- `/stats` - Tool statistics
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
src/
|
|
├── index.ts # Stdio transport entry point
|
|
├── index-http.ts # HTTP transport entry point
|
|
├── pg-client.ts # PostgreSQL client wrapper
|
|
├── config/
|
|
│ └── database.ts # DB configuration
|
|
├── server/
|
|
│ ├── index.ts # Server module exports
|
|
│ ├── create-server.ts # MCP server factory
|
|
│ └── register-handlers.ts # Shared handler registration
|
|
├── types/
|
|
│ ├── index.ts
|
|
│ ├── tools.ts # Base tool types
|
|
│ └── db.ts # Database table types
|
|
├── tools/
|
|
│ ├── index.ts # Export all tools
|
|
│ ├── documents.ts # 19 tools - Core document management
|
|
│ ├── collections.ts # 14 tools - Collection management
|
|
│ ├── users.ts # 9 tools - User management
|
|
│ ├── groups.ts # 8 tools - Group management
|
|
│ ├── comments.ts # 6 tools - Comment system
|
|
│ ├── shares.ts # 5 tools - Document sharing
|
|
│ ├── revisions.ts # 3 tools - Version history
|
|
│ ├── events.ts # 3 tools - Audit log
|
|
│ ├── attachments.ts # 5 tools - File attachments
|
|
│ ├── file-operations.ts # 4 tools - Import/export jobs
|
|
│ ├── oauth.ts # 8 tools - OAuth management
|
|
│ ├── auth.ts # 2 tools - Authentication
|
|
│ ├── stars.ts # 3 tools - Bookmarks
|
|
│ ├── pins.ts # 3 tools - Pinned documents
|
|
│ ├── views.ts # 2 tools - View tracking
|
|
│ ├── reactions.ts # 3 tools - Emoji reactions
|
|
│ ├── api-keys.ts # 4 tools - API keys
|
|
│ ├── webhooks.ts # 4 tools - Webhooks
|
|
│ ├── backlinks.ts # 1 tool - Link references
|
|
│ ├── search-queries.ts # 2 tools - Search analytics
|
|
│ ├── teams.ts # 5 tools - Team/workspace
|
|
│ ├── integrations.ts # 6 tools - External integrations
|
|
│ ├── notifications.ts # 4 tools - Notifications
|
|
│ ├── subscriptions.ts # 4 tools - Subscriptions
|
|
│ ├── templates.ts # 5 tools - Templates
|
|
│ ├── imports-tools.ts # 4 tools - Import jobs
|
|
│ ├── emojis.ts # 3 tools - Custom emojis
|
|
│ ├── user-permissions.ts # 3 tools - Permissions
|
|
│ ├── bulk-operations.ts # 6 tools - Batch operations
|
|
│ ├── advanced-search.ts # 6 tools - Advanced search
|
|
│ ├── analytics.ts # 6 tools - Analytics
|
|
│ ├── export-import.ts # 2 tools - Markdown export/import
|
|
│ └── desk-sync.ts # 2 tools - Desk CRM integration
|
|
└── utils/
|
|
├── index.ts # Export all utilities
|
|
├── logger.ts # Logging utility
|
|
├── security.ts # Security utilities (validation, rate limiting)
|
|
├── transaction.ts # Transaction helpers with retry logic
|
|
├── query-builder.ts # Safe parameterized query builder
|
|
├── validation.ts # Zod-based input validation
|
|
├── audit.ts # Audit logging for write operations
|
|
├── monitoring.ts # Connection pool health monitoring
|
|
└── pagination.ts # Cursor-based pagination helpers
|
|
```
|
|
|
|
## Tools Summary (164 total)
|
|
|
|
| Module | Tools | Description |
|
|
|--------|-------|-------------|
|
|
| documents | 19 | CRUD, search, archive, move, templates, memberships |
|
|
| collections | 14 | CRUD, memberships, groups, export |
|
|
| users | 9 | CRUD, suspend, activate, promote, demote |
|
|
| groups | 8 | CRUD, memberships |
|
|
| comments | 6 | CRUD, resolve |
|
|
| shares | 5 | CRUD, revoke |
|
|
| revisions | 3 | list, info, compare |
|
|
| events | 3 | list, info, stats |
|
|
| attachments | 5 | CRUD, stats |
|
|
| file-operations | 4 | import/export jobs |
|
|
| oauth | 8 | OAuth clients, authentications |
|
|
| auth | 2 | auth info, config |
|
|
| stars | 3 | list, create, delete (bookmarks) |
|
|
| pins | 3 | list, create, delete (highlighted docs) |
|
|
| views | 2 | list, create (view tracking) |
|
|
| reactions | 3 | list, create, delete (emoji on comments) |
|
|
| api-keys | 4 | CRUD (programmatic access) |
|
|
| webhooks | 4 | CRUD (event subscriptions) |
|
|
| backlinks | 1 | list (document links - read-only view) |
|
|
| search-queries | 2 | list, stats (search analytics) |
|
|
| teams | 5 | get, update, stats, domains, settings |
|
|
| integrations | 6 | list, get, create, update, delete, sync |
|
|
| notifications | 4 | list, mark read, mark all read, settings |
|
|
| subscriptions | 4 | list, subscribe, unsubscribe, settings |
|
|
| templates | 5 | list, get, create from, convert to/from |
|
|
| imports | 4 | list, status, create, cancel |
|
|
| emojis | 3 | list, create, delete |
|
|
| user-permissions | 3 | list, grant, revoke |
|
|
| bulk-operations | 6 | archive, delete, move, restore, add/remove users |
|
|
| advanced-search | 6 | advanced search, facets, recent, user activity, orphaned, duplicates |
|
|
| analytics | 6 | overview, user activity, content insights, collection stats, growth, search |
|
|
| export-import | 2 | export collection to markdown, import markdown folder |
|
|
| desk-sync | 2 | create desk project doc, link desk task |
|
|
|
|
## Configuration
|
|
|
|
### Production (hub.descomplicar.pt)
|
|
|
|
**Requires SSH tunnel** - Run before starting Claude Code:
|
|
```bash
|
|
./start-tunnel.sh start
|
|
```
|
|
|
|
Add to `~/.claude.json` under `mcpServers`:
|
|
|
|
```json
|
|
{
|
|
"outline-postgresql": {
|
|
"command": "node",
|
|
"args": ["/home/ealmeida/mcp-servers/mcp-outline-postgresql/dist/index.js"],
|
|
"env": {
|
|
"DATABASE_URL": "postgres://postgres:***@localhost:5433/descomplicar",
|
|
"LOG_LEVEL": "error"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Local Development
|
|
|
|
```json
|
|
{
|
|
"outline-postgresql": {
|
|
"command": "node",
|
|
"args": ["/home/ealmeida/mcp-servers/mcp-outline-postgresql/dist/index.js"],
|
|
"env": {
|
|
"DATABASE_URL": "postgres://outline:outline_dev_2026@localhost:5432/outline",
|
|
"LOG_LEVEL": "error"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## SSH Tunnel Management
|
|
|
|
```bash
|
|
# Start tunnel (before Claude Code)
|
|
./start-tunnel.sh start
|
|
|
|
# Check status
|
|
./start-tunnel.sh status
|
|
|
|
# Stop tunnel
|
|
./start-tunnel.sh stop
|
|
```
|
|
|
|
## Environment
|
|
|
|
| Environment | Port | Database | Tunnel |
|
|
|-------------|------|----------|--------|
|
|
| Production | 5433 | descomplicar | Required |
|
|
| Development | 5432 | outline | No |
|
|
|
|
## Key Patterns
|
|
|
|
### Tool Response Format
|
|
|
|
```typescript
|
|
return {
|
|
content: [{
|
|
type: 'text',
|
|
text: JSON.stringify(data, null, 2)
|
|
}]
|
|
};
|
|
```
|
|
|
|
### Naming Conventions
|
|
|
|
| Type | Convention | Example |
|
|
|------|------------|---------|
|
|
| Tool name | snake_case with prefix | `outline_list_documents` |
|
|
| Function | camelCase | `listDocuments` |
|
|
| Type | PascalCase | `DocumentRow` |
|
|
| File | kebab-case | `documents.ts` |
|
|
|
|
## Database
|
|
|
|
**PostgreSQL 15** - Direct SQL access (not Outline API)
|
|
|
|
Key tables: `documents`, `collections`, `users`, `groups`, `comments`, `revisions`, `shares`, `attachments`, `events`, `stars`, `pins`, `views`, `file_operations`, `oauth_clients`
|
|
|
|
Soft deletes: Most entities use `deletedAt` column, not hard deletes.
|
|
|
|
See `SPEC-MCP-OUTLINE.md` for complete database schema.
|
|
|
|
## Security Utilities
|
|
|
|
The `src/utils/security.ts` module provides essential security functions:
|
|
|
|
### Validation Functions
|
|
|
|
| Function | Description |
|
|
|----------|-------------|
|
|
| `isValidUUID(uuid)` | Validate UUID format |
|
|
| `isValidUrlId(urlId)` | Validate URL-safe ID format |
|
|
| `isValidEmail(email)` | Validate email format |
|
|
| `isValidHttpUrl(url)` | Validate URL is HTTP(S) - rejects javascript:, data:, file: protocols |
|
|
| `isValidISODate(date)` | Validate ISO date format (YYYY-MM-DD or full ISO) |
|
|
| `validateDaysInterval(days, default, max)` | Validate and clamp days interval for SQL |
|
|
| `validatePeriod(period, allowed, default)` | Validate period against allowed values |
|
|
| `validatePagination(limit, offset)` | Validate and normalize pagination params |
|
|
| `validateSortDirection(direction)` | Validate sort direction (ASC/DESC) |
|
|
| `validateSortField(field, allowed, default)` | Validate sort field against whitelist |
|
|
|
|
### Sanitization Functions
|
|
|
|
| Function | Description |
|
|
|----------|-------------|
|
|
| `sanitizeInput(input)` | Remove null bytes and trim whitespace |
|
|
| `escapeHtml(text)` | Escape HTML entities for safe display |
|
|
|
|
### Rate Limiting
|
|
|
|
| Function | Description |
|
|
|----------|-------------|
|
|
| `checkRateLimit(type, clientId)` | Check if request should be rate limited |
|
|
| `startRateLimitCleanup()` | Start background cleanup of expired entries |
|
|
| `stopRateLimitCleanup()` | Stop cleanup interval (call on shutdown) |
|
|
| `clearRateLimitStore()` | Clear all rate limit entries (testing) |
|
|
|
|
### Usage Example
|
|
|
|
```typescript
|
|
import {
|
|
isValidUUID,
|
|
isValidHttpUrl,
|
|
validateDaysInterval,
|
|
startRateLimitCleanup,
|
|
stopRateLimitCleanup
|
|
} from './utils/security.js';
|
|
|
|
// Validation before SQL
|
|
if (!isValidUUID(args.user_id)) {
|
|
throw new Error('Invalid user_id format');
|
|
}
|
|
|
|
// URL validation (prevents XSS)
|
|
if (!isValidHttpUrl(args.webhook_url)) {
|
|
throw new Error('Invalid URL. Only HTTP(S) allowed.');
|
|
}
|
|
|
|
// Safe interval for SQL
|
|
const safeDays = validateDaysInterval(args.days, 30, 365);
|
|
// Use in query: `INTERVAL '${safeDays} days'` is safe (it's a number)
|
|
|
|
// Lifecycle management
|
|
startRateLimitCleanup(); // On server start
|
|
stopRateLimitCleanup(); // On graceful shutdown
|
|
```
|
|
|
|
## Cryptographic Security
|
|
|
|
Secrets and tokens use `crypto.randomBytes()` instead of `Math.random()`:
|
|
|
|
- **OAuth secrets:** `oauth.ts` - `sk_` prefixed base64url tokens
|
|
- **API keys:** `api-keys.ts` - `ol_` prefixed keys, only hash stored in DB
|
|
- **Share URLs:** `shares.ts` - Cryptographically secure URL IDs
|