Documents created via MCP were not visible in Outline interface. Outline requires an entry in the revisions table to display documents. Now uses transaction to insert into both documents and revisions tables. 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.9
|
|
**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
|