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>
23 KiB
23 KiB
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
# 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
{
"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
{
"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
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
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)
-- 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)
# 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:
list_documents,get_document,search_documentslist_collections,get_collectionlist_users,get_usercreate_document,update_documentcreate_collection,update_collection
Fase 3: Secondary Tools (P2)
- Comments (CRUD)
- Memberships
- Revisions
- Shares
- Events
Fase 4: Advanced Tools (P3)
- Templates
- Attachments
- OAuth
- Import/Export
9. Configuração Claude Code
~/.claude.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
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
// SEMPRE retornar neste formato
return {
content: [{
type: 'text',
text: JSON.stringify(data, null, 2)
}]
};
Error Handling
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
Documento gerado: 2026-01-31 Autor: Descomplicar®