Files
mcp-outline-postgresql/SPEC-MCP-OUTLINE.md
Emanuel Almeida b05b54033f feat: Initial release MCP Outline PostgreSQL v1.0.0
86 tools across 12 modules for direct PostgreSQL access to Outline Wiki:
- Documents (19), Collections (14), Users (9), Groups (8)
- Comments (6), Shares (5), Revisions (3), Events (3)
- Attachments (5), File Operations (4), OAuth (8), Auth (2)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 13:25:09 +00:00

843 lines
23 KiB
Markdown

# MCP Outline - Especificação Completa
**Versão:** 1.0.0
**Data:** 2026-01-31
**Autor:** Descomplicar®
---
## 1. Visão Geral
### Objectivo
MCP para acesso directo à base de dados PostgreSQL do Outline, seguindo os padrões estabelecidos pelo `mcp-desk-crm-sql-v3`.
### Arquitectura
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Claude Code │────▶│ MCP Outline │────▶│ PostgreSQL │
│ (Cliente) │◀────│ (Servidor) │◀────│ (Outline DB) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ MCP Protocol │ SQL Directo
│ (stdio/SSE) │ (pg client)
```
### Decisões Técnicas
| Aspecto | Decisão | Razão |
|---------|---------|-------|
| **Acesso** | SQL Directo (não API) | Performance, controlo total |
| **BD** | PostgreSQL 15 | Compatível com Outline |
| **Transporte** | stdio (dev), SSE (prod) | Padrão MCP |
| **Linguagem** | TypeScript | Type safety, padrão Desk |
---
## 2. Ambiente de Desenvolvimento
### Configuração Local
```bash
# Base de dados
Host: localhost
Port: 5432
User: outline
Password: outline_dev_2026
Database: outline
# Connection string
postgres://outline:outline_dev_2026@localhost:5432/outline
```
### Estrutura do Projecto
```
~/mcp-servers/mcp-outline/
├── package.json
├── tsconfig.json
├── .env
├── .env.example
├── CHANGELOG.md
├── README.md
├── SPEC-MCP-OUTLINE.md ← Este ficheiro
├── src/
│ ├── index.ts ← Entry point MCP
│ ├── pg-client.ts ← Cliente PostgreSQL
│ ├── config/
│ │ └── database.ts ← Configuração BD
│ ├── types/
│ │ ├── index.ts
│ │ ├── tools.ts ← Tipos base das tools
│ │ └── db.ts ← Tipos das tabelas
│ ├── tools/
│ │ ├── index.ts ← Exporta todas as tools
│ │ ├── documents.ts
│ │ ├── collections.ts
│ │ ├── users.ts
│ │ ├── groups.ts
│ │ ├── comments.ts
│ │ ├── attachments.ts
│ │ ├── shares.ts
│ │ ├── revisions.ts
│ │ └── events.ts
│ └── utils/
│ ├── logger.ts
│ └── security.ts
├── dist/ ← Build output
└── tests/
└── tools/
```
---
## 3. Dependências
### package.json
```json
{
"name": "mcp-outline",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "tsx watch src/index.ts",
"test": "vitest"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"pg": "^8.11.0",
"dotenv": "^16.0.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"@types/pg": "^8.10.0",
"typescript": "^5.0.0",
"tsx": "^4.0.0",
"vitest": "^1.0.0"
}
}
```
### tsconfig.json
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
```
---
## 4. Tipos Base (Padrão Desk)
### src/types/tools.ts
```typescript
import { Pool } from 'pg';
export interface ToolResponse {
content: Array<{
type: 'text';
text: string;
}>;
[key: string]: unknown;
}
export interface BaseTool<TArgs = Record<string, unknown>> {
name: string;
description: string;
inputSchema: {
type: string;
properties: Record<string, unknown>;
required?: string[];
};
handler: (args: TArgs, pgClient: Pool) => Promise<ToolResponse>;
}
// Argumentos comuns
export interface PaginationArgs {
limit?: number;
offset?: number;
}
export interface DateRangeArgs {
dateFrom?: string;
dateTo?: string;
}
```
---
## 5. API Outline → Tools MCP
### 5.1 Documents (17 tools)
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `documents.list` | `list_documents` | SELECT | P1 |
| `documents.info` | `get_document` | SELECT | P1 |
| `documents.create` | `create_document` | INSERT | P1 |
| `documents.update` | `update_document` | UPDATE | P1 |
| `documents.delete` | `delete_document` | UPDATE (soft) | P1 |
| `documents.search` | `search_documents` | SELECT FTS | P1 |
| `documents.drafts` | `list_drafts` | SELECT | P2 |
| `documents.viewed` | `list_viewed_documents` | SELECT | P2 |
| `documents.archive` | `archive_document` | UPDATE | P2 |
| `documents.restore` | `restore_document` | UPDATE | P2 |
| `documents.move` | `move_document` | UPDATE | P2 |
| `documents.unpublish` | `unpublish_document` | UPDATE | P3 |
| `documents.templatize` | `templatize_document` | INSERT | P3 |
| `documents.export` | `export_document` | SELECT | P2 |
| `documents.import` | `import_document` | INSERT | P3 |
| `documents.users` | `list_document_users` | SELECT | P2 |
| `documents.memberships` | `list_document_memberships` | SELECT | P3 |
#### Exemplo: list_documents
```typescript
export const listDocuments: BaseTool = {
name: 'list_documents',
description: 'Lista documentos com filtros. Suporta paginação e ordenação.',
inputSchema: {
type: 'object',
properties: {
collection_id: {
type: 'string',
description: 'UUID da collection para filtrar'
},
user_id: {
type: 'string',
description: 'UUID do autor para filtrar'
},
template: {
type: 'boolean',
description: 'Filtrar apenas templates'
},
published: {
type: 'boolean',
description: 'Filtrar apenas publicados (default: true)'
},
limit: {
type: 'number',
description: 'Máximo de resultados (default: 25, max: 100)'
},
offset: {
type: 'number',
description: 'Offset para paginação'
},
sort: {
type: 'string',
enum: ['updatedAt', 'createdAt', 'title', 'index'],
description: 'Campo para ordenação'
},
direction: {
type: 'string',
enum: ['ASC', 'DESC'],
description: 'Direcção da ordenação'
}
}
},
handler: async (args, pgClient) => {
const limit = Math.min(args.limit || 25, 100);
const offset = args.offset || 0;
const sort = args.sort || 'updatedAt';
const direction = args.direction || 'DESC';
let sql = `
SELECT
d.id,
d.title,
d.text,
d."collectionId",
d."parentDocumentId",
d."createdById",
d."publishedAt",
d."updatedAt",
d."archivedAt",
d.template,
c.name as collection_name,
u.name as author_name
FROM documents d
LEFT JOIN collections c ON d."collectionId" = c.id
LEFT JOIN users u ON d."createdById" = u.id
WHERE d."deletedAt" IS NULL
`;
const params: any[] = [];
let paramIndex = 1;
if (args.collection_id) {
sql += ` AND d."collectionId" = $${paramIndex++}`;
params.push(args.collection_id);
}
if (args.user_id) {
sql += ` AND d."createdById" = $${paramIndex++}`;
params.push(args.user_id);
}
if (args.template !== undefined) {
sql += ` AND d.template = $${paramIndex++}`;
params.push(args.template);
}
if (args.published !== false) {
sql += ` AND d."publishedAt" IS NOT NULL`;
}
sql += ` ORDER BY d."${sort}" ${direction}`;
sql += ` LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
params.push(limit, offset);
const result = await pgClient.query(sql, params);
return {
content: [{
type: 'text',
text: JSON.stringify({
documents: result.rows,
pagination: { limit, offset, count: result.rows.length }
}, null, 2)
}]
};
}
};
```
### 5.2 Collections (13 tools)
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `collections.list` | `list_collections` | SELECT | P1 |
| `collections.info` | `get_collection` | SELECT | P1 |
| `collections.create` | `create_collection` | INSERT | P1 |
| `collections.update` | `update_collection` | UPDATE | P1 |
| `collections.delete` | `delete_collection` | UPDATE (soft) | P2 |
| `collections.documents` | `list_collection_documents` | SELECT | P1 |
| `collections.add_user` | `add_user_to_collection` | INSERT | P2 |
| `collections.remove_user` | `remove_user_from_collection` | DELETE | P2 |
| `collections.memberships` | `list_collection_memberships` | SELECT | P2 |
| `collections.add_group` | `add_group_to_collection` | INSERT | P3 |
| `collections.remove_group` | `remove_group_from_collection` | DELETE | P3 |
| `collections.group_memberships` | `list_collection_group_memberships` | SELECT | P3 |
| `collections.export` | `export_collection` | SELECT | P3 |
### 5.3 Users (7 tools)
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `users.list` | `list_users` | SELECT | P1 |
| `users.info` | `get_user` | SELECT | P1 |
| `users.create` | `create_user` | INSERT | P2 |
| `users.update` | `update_user` | UPDATE | P2 |
| `users.delete` | `delete_user` | UPDATE (soft) | P3 |
| `users.suspend` | `suspend_user` | UPDATE | P2 |
| `users.activate` | `activate_user` | UPDATE | P2 |
### 5.4 Groups (7 tools)
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `groups.list` | `list_groups` | SELECT | P1 |
| `groups.info` | `get_group` | SELECT | P1 |
| `groups.create` | `create_group` | INSERT | P2 |
| `groups.update` | `update_group` | UPDATE | P2 |
| `groups.delete` | `delete_group` | DELETE | P3 |
| `groups.memberships` | `list_group_members` | SELECT | P2 |
| `groups.add_user` | `add_user_to_group` | INSERT | P2 |
### 5.5 Comments (5 tools)
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `comments.list` | `list_comments` | SELECT | P1 |
| `comments.info` | `get_comment` | SELECT | P2 |
| `comments.create` | `create_comment` | INSERT | P1 |
| `comments.update` | `update_comment` | UPDATE | P2 |
| `comments.delete` | `delete_comment` | DELETE | P2 |
### 5.6 Shares (4 tools)
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `shares.info` | `get_share` | SELECT | P2 |
| `shares.create` | `create_share` | INSERT | P2 |
| `shares.update` | `update_share` | UPDATE | P3 |
| `shares.delete` | `delete_share` | DELETE | P2 |
### 5.7 Revisions (2 tools)
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `revisions.list` | `list_revisions` | SELECT | P2 |
| `revisions.info` | `get_revision` | SELECT | P2 |
### 5.8 Events (1 tool)
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `events.list` | `list_events` | SELECT | P2 |
### 5.9 Attachments (3 tools)
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `attachments.create` | `create_attachment` | INSERT | P3 |
| `attachments.redirect` | `get_attachment_url` | SELECT | P3 |
| `attachments.delete` | `delete_attachment` | DELETE | P3 |
### 5.10 Auth (2 tools)
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `auth.info` | `get_auth_info` | SELECT | P3 |
| `auth.config` | `get_auth_config` | SELECT | P3 |
### 5.11 Stars (3 tools) - NOVO
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `stars.list` | `list_stars` | SELECT | P2 |
| `stars.create` | `star_document` | INSERT | P2 |
| `stars.delete` | `unstar_document` | DELETE | P2 |
### 5.12 Pins (3 tools) - NOVO
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `pins.list` | `list_pins` | SELECT | P2 |
| `pins.create` | `pin_document` | INSERT | P2 |
| `pins.delete` | `unpin_document` | DELETE | P2 |
### 5.13 Views (2 tools) - NOVO
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `views.list` | `list_document_views` | SELECT | P2 |
| `views.create` | `register_view` | INSERT | P3 |
### 5.14 Reactions (3 tools) - NOVO
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `reactions.list` | `list_reactions` | SELECT | P3 |
| `reactions.create` | `add_reaction` | INSERT | P3 |
| `reactions.delete` | `remove_reaction` | DELETE | P3 |
### 5.15 API Keys (4 tools) - NOVO
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `apiKeys.list` | `list_api_keys` | SELECT | P2 |
| `apiKeys.create` | `create_api_key` | INSERT | P2 |
| `apiKeys.delete` | `delete_api_key` | DELETE | P2 |
| `apiKeys.info` | `get_api_key` | SELECT | P3 |
### 5.16 Webhooks (4 tools) - NOVO
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `webhooks.list` | `list_webhooks` | SELECT | P3 |
| `webhooks.create` | `create_webhook` | INSERT | P3 |
| `webhooks.update` | `update_webhook` | UPDATE | P3 |
| `webhooks.delete` | `delete_webhook` | DELETE | P3 |
### 5.17 Backlinks (1 tool) - NOVO
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `backlinks.list` | `list_backlinks` | SELECT | P2 |
### 5.18 Search Queries (2 tools) - NOVO
| API Endpoint | Tool MCP | Operação | Prioridade |
|--------------|----------|----------|------------|
| `searchQueries.list` | `list_search_queries` | SELECT | P3 |
| `searchQueries.popular` | `get_popular_searches` | SELECT | P3 |
---
## 6. Resumo de Tools
### Por Prioridade
| Prioridade | Quantidade | Descrição |
|------------|------------|-----------|
| P1 | 18 | Core: CRUD documentos, collections, users, search |
| P2 | 37 | Secundárias: memberships, comments, shares, stars, pins, views, apiKeys |
| P3 | 28 | Avançadas: templates, OAuth, attachments, reactions, webhooks |
| **Total** | **83** | |
### Por Módulo
| Módulo | Tools | Estado |
|--------|-------|--------|
| Documents | 17 | A implementar |
| Collections | 13 | A implementar |
| Users | 7 | A implementar |
| Groups | 7 | A implementar |
| Comments | 5 | A implementar |
| Shares | 4 | A implementar |
| Revisions | 2 | A implementar |
| Events | 1 | A implementar |
| Attachments | 3 | A implementar |
| Auth | 2 | A implementar |
| Stars | 3 | A implementar |
| Pins | 3 | A implementar |
| Views | 2 | A implementar |
| Reactions | 3 | A implementar |
| API Keys | 4 | A implementar |
| Webhooks | 4 | A implementar |
| Backlinks | 1 | A implementar |
| Search Queries | 2 | A implementar |
---
## 7. Schema PostgreSQL (Outline)
### Tabelas Reais (41 tabelas - verificado 2026-01-31)
```sql
-- Core Content
documents -- Documentos e drafts
collections -- Organizações de documentos
revisions -- Histórico de versões
comments -- Comentários em documentos
attachments -- Ficheiros anexados
backlinks -- Links entre documentos
-- Users & Auth
users -- Utilizadores
teams -- Workspaces
groups -- Grupos de utilizadores
group_users -- Membros de grupos
user_permissions -- Permissões de utilizadores
user_authentications -- Autenticações de utilizadores
user_passkeys -- Passkeys WebAuthn
authentications -- Sessões de autenticação
authentication_providers -- Providers OAuth
oauth_authentications -- Tokens OAuth
oauth_authorization_codes -- Códigos OAuth
oauth_clients -- Clientes OAuth registados
apiKeys -- API tokens
-- Permissions
collection_users -- Permissões collection-user
collection_groups -- Permissões collection-group
group_permissions -- Permissões de grupos
-- Sharing & Social
shares -- Links públicos
stars -- Favoritos
pins -- Documentos fixados
views -- Visualizações de documentos
reactions -- Reacções (emojis em docs)
emojis -- Emojis customizados
relationships -- Relações entre entidades
-- Notifications & Events
events -- Audit log
notifications -- Alertas
subscriptions -- Subscrições de notificações
-- Import/Export
imports -- Imports em curso
import_tasks -- Tarefas de import
file_operations -- Operações de ficheiros
-- Integrations
integrations -- Integrações externas
webhook_subscriptions -- Webhooks registados
webhook_deliveries -- Entregas de webhooks
-- System
SequelizeMeta -- Migrações Sequelize
team_domains -- Domínios por team
search_queries -- Histórico de pesquisas
```
### Relações Chave
```
teams (1) ─────────< (N) users
teams (1) ─────────< (N) collections
teams (1) ─────────< (N) groups
teams (1) ─────────< (N) team_domains
teams (1) ─────────< (N) integrations
collections (1) ────< (N) documents
collections (1) ────< (N) collection_users
collections (1) ────< (N) collection_groups
documents (1) ──────< (N) comments
documents (1) ──────< (N) revisions
documents (1) ──────< (N) shares
documents (1) ──────< (N) attachments
documents (1) ──────< (N) views
documents (1) ──────< (N) stars
documents (1) ──────< (N) pins
documents (1) ──────< (N) reactions
documents (1) ──────< (N) backlinks
users (1) ──────────< (N) documents (createdById)
users (1) ──────────< (N) apiKeys
users (1) ──────────< (N) user_authentications
users (1) ──────────< (N) notifications
users (1) ──────────< (N) subscriptions
users (N) ─────────<>───── (N) groups (via group_users)
groups (N) ────────<>───── (N) collections (via collection_groups)
integrations (1) ───< (N) webhook_subscriptions
webhook_subscriptions (1) < (N) webhook_deliveries
```
---
## 8. Implementação
### Fase 1: Setup (MVP)
```bash
# 1. Criar estrutura
mkdir -p ~/mcp-servers/mcp-outline/{src/{tools,types,config,utils},tests}
cd ~/mcp-servers/mcp-outline
# 2. Inicializar
npm init -y
npm install @modelcontextprotocol/sdk pg dotenv
npm install -D typescript @types/node @types/pg tsx vitest
# 3. Configurar
cp .env.example .env
# Editar .env com credenciais
# 4. Build
npm run build
# 5. Testar
npm run dev
```
### Fase 2: Core Tools (P1)
Implementar por ordem:
1. `list_documents`, `get_document`, `search_documents`
2. `list_collections`, `get_collection`
3. `list_users`, `get_user`
4. `create_document`, `update_document`
5. `create_collection`, `update_collection`
### Fase 3: Secondary Tools (P2)
1. Comments (CRUD)
2. Memberships
3. Revisions
4. Shares
5. Events
### Fase 4: Advanced Tools (P3)
1. Templates
2. Attachments
3. OAuth
4. Import/Export
---
## 9. Configuração Claude Code
### ~/.claude.json
```json
{
"mcpServers": {
"outline": {
"command": "node",
"args": ["/home/ealmeida/mcp-servers/mcp-outline/dist/index.js"],
"env": {
"DATABASE_URL": "postgres://outline:outline_dev_2026@localhost:5432/outline"
}
}
}
}
```
---
## 10. Testes
### Estrutura
```
tests/
├── setup.ts # Configuração vitest
├── tools/
│ ├── documents.test.ts
│ ├── collections.test.ts
│ └── users.test.ts
└── integration/
└── full-flow.test.ts
```
### Exemplo Test
```typescript
import { describe, it, expect, beforeAll } from 'vitest';
import { Pool } from 'pg';
import { listDocuments } from '../src/tools/documents';
describe('Documents Tools', () => {
let pool: Pool;
beforeAll(() => {
pool = new Pool({
connectionString: process.env.DATABASE_URL
});
});
it('list_documents returns array', async () => {
const result = await listDocuments.handler({}, pool);
const data = JSON.parse(result.content[0].text);
expect(Array.isArray(data.documents)).toBe(true);
expect(data.pagination).toBeDefined();
});
});
```
---
## 11. Padrões de Código (do Desk)
### Response Format
```typescript
// SEMPRE retornar neste formato
return {
content: [{
type: 'text',
text: JSON.stringify(data, null, 2)
}]
};
```
### Error Handling
```typescript
try {
const result = await pgClient.query(sql, params);
return {
content: [{
type: 'text',
text: JSON.stringify({ success: true, data: result.rows }, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
}, null, 2)
}]
};
}
```
### Naming Conventions
| Tipo | Padrão | Exemplo |
|------|--------|---------|
| Tool name | snake_case | `list_documents` |
| Function | camelCase | `listDocuments` |
| Type | PascalCase | `DocumentRow` |
| File | kebab-case | `documents.ts` |
---
## 12. Checklist de Implementação
### Setup
- [ ] Estrutura de pastas criada
- [ ] package.json configurado
- [ ] tsconfig.json configurado
- [ ] .env configurado
- [ ] pg-client.ts implementado
### Tools P1
- [ ] list_documents
- [ ] get_document
- [ ] create_document
- [ ] update_document
- [ ] delete_document
- [ ] search_documents
- [ ] list_collections
- [ ] get_collection
- [ ] create_collection
- [ ] update_collection
- [ ] list_collection_documents
- [ ] list_users
- [ ] get_user
- [ ] list_groups
- [ ] get_group
- [ ] list_comments
- [ ] create_comment
- [ ] list_group_members
### Tools P2 (novas)
- [ ] list_stars
- [ ] star_document
- [ ] unstar_document
- [ ] list_pins
- [ ] pin_document
- [ ] unpin_document
- [ ] list_document_views
- [ ] list_backlinks
- [ ] list_api_keys
- [ ] create_api_key
- [ ] delete_api_key
### Integração
- [ ] Build sem erros
- [ ] Configurado em ~/.claude.json
- [ ] Tools visíveis no Claude Code
- [ ] Testes P1 passam
### Documentação
- [ ] README.md
- [ ] CHANGELOG.md
- [ ] Obsidian docs actualizados
---
## 13. Referências
- [Outline API Docs](https://www.getoutline.com/developers)
- [MCP SDK](https://modelcontextprotocol.io/)
- [mcp-desk-crm-sql-v3](~/mcp-servers/mcp-desk-crm-sql-v3/)
- [PostgreSQL pg docs](https://node-postgres.com/)
---
*Documento gerado: 2026-01-31*
*Autor: Descomplicar®*