feat: v1.3.1 - Multi-transport + Production deployment

- Add HTTP transport (StreamableHTTPServerTransport)
- Add shared server module (src/server/)
- Configure production for hub.descomplicar.pt
- Add SSH tunnel script (start-tunnel.sh)
- Fix connection leak in pg-client.ts
- Fix atomicity bug in comments deletion
- Update docs with test plan for 164 tools

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 17:06:30 +00:00
parent 0329a1179a
commit 5f49cb63e8
15 changed files with 1381 additions and 464 deletions

View File

@@ -1,127 +1,21 @@
#!/usr/bin/env node
/**
* MCP Outline PostgreSQL - Main Server
* MCP Outline PostgreSQL - Stdio Server
* Standard stdio transport for CLI/local access
* @author Descomplicar® | @link descomplicar.pt | @copyright 2026
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
ListToolsRequestSchema,
CallToolRequestSchema,
ListResourcesRequestSchema,
ListPromptsRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import * as dotenv from 'dotenv';
import { PgClient } from './pg-client.js';
import { getDatabaseConfig } from './config/database.js';
import { createMcpServer, allTools, getToolCounts } from './server/index.js';
import { logger } from './utils/logger.js';
import { checkRateLimit, startRateLimitCleanup, stopRateLimitCleanup } from './utils/security.js';
import { BaseTool } from './types/tools.js';
// Import ALL tools
import {
documentsTools,
collectionsTools,
usersTools,
groupsTools,
commentsTools,
sharesTools,
revisionsTools,
eventsTools,
attachmentsTools,
fileOperationsTools,
oauthTools,
authTools,
starsTools,
pinsTools,
viewsTools,
reactionsTools,
apiKeysTools,
webhooksTools,
backlinksTools,
searchQueriesTools,
// New modules
teamsTools,
integrationsTools,
notificationsTools,
subscriptionsTools,
templatesTools,
importsTools,
emojisTools,
userPermissionsTools,
bulkOperationsTools,
advancedSearchTools,
analyticsTools,
exportImportTools,
deskSyncTools
} from './tools/index.js';
import { startRateLimitCleanup, stopRateLimitCleanup } from './utils/security.js';
dotenv.config();
// Combine ALL tools into single array
const allTools: BaseTool[] = [
// Core functionality
...documentsTools,
...collectionsTools,
...usersTools,
...groupsTools,
// Collaboration
...commentsTools,
...sharesTools,
...revisionsTools,
// System
...eventsTools,
...attachmentsTools,
...fileOperationsTools,
// Authentication
...oauthTools,
...authTools,
// User engagement
...starsTools,
...pinsTools,
...viewsTools,
...reactionsTools,
// API & Integration
...apiKeysTools,
...webhooksTools,
...integrationsTools,
// Analytics & Search
...backlinksTools,
...searchQueriesTools,
...advancedSearchTools,
...analyticsTools,
// Teams & Workspace
...teamsTools,
// Notifications & Subscriptions
...notificationsTools,
...subscriptionsTools,
// Templates & Imports
...templatesTools,
...importsTools,
// Custom content
...emojisTools,
// Permissions & Bulk operations
...userPermissionsTools,
...bulkOperationsTools,
// Export/Import & External Sync
...exportImportTools,
...deskSyncTools
];
// Validate all tools have required properties
const invalidTools = allTools.filter((tool) => !tool.name || !tool.handler);
if (invalidTools.length > 0) {
@@ -142,90 +36,16 @@ async function main() {
throw new Error('Failed to connect to PostgreSQL database');
}
// Initialize MCP server
const server = new Server({
name: 'mcp-outline',
version: '1.0.0'
// Create MCP server with shared configuration
const server = createMcpServer(pgClient.getPool(), {
name: 'mcp-outline-postgresql',
version: '1.3.1'
});
// Set capabilities (required for MCP v2.2+)
(server as any)._capabilities = {
tools: {},
resources: {},
prompts: {}
};
// Connect transport BEFORE registering handlers
// Connect stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);
// Register tools list handler
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: allTools.map((tool) => ({
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema
}))
}));
// Register resources handler (required even if empty)
server.setRequestHandler(ListResourcesRequestSchema, async () => {
logger.debug('Resources list requested');
return { resources: [] };
});
// Register prompts handler (required even if empty)
server.setRequestHandler(ListPromptsRequestSchema, async () => {
logger.debug('Prompts list requested');
return { prompts: [] };
});
// Register tool call handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
// Rate limiting (using 'default' as clientId for now)
const clientId = process.env.CLIENT_ID || 'default';
if (!checkRateLimit('api', clientId)) {
return {
content: [
{ type: 'text', text: 'Too Many Requests: rate limit exceeded. Try again later.' }
]
};
}
// Find the tool handler
const tool = allTools.find((t) => t.name === name);
if (!tool) {
return {
content: [
{
type: 'text',
text: `Tool '${name}' not found`
}
]
};
}
try {
// Pass the pool directly to tool handlers
return await tool.handler(args as Record<string, unknown>, pgClient.getPool());
} catch (error) {
logger.error(`Error in tool ${name}:`, {
error: error instanceof Error ? error.message : String(error)
});
return {
content: [
{
type: 'text',
text: `Error in tool ${name}: ${error instanceof Error ? error.message : String(error)}`
}
]
};
}
});
// Start background tasks
startRateLimitCleanup();
@@ -246,42 +66,9 @@ async function main() {
// Debug logging
logger.debug('MCP Outline PostgreSQL Server running', {
transport: 'stdio',
totalTools: allTools.length,
toolsByModule: {
documents: documentsTools.length,
collections: collectionsTools.length,
users: usersTools.length,
groups: groupsTools.length,
comments: commentsTools.length,
shares: sharesTools.length,
revisions: revisionsTools.length,
events: eventsTools.length,
attachments: attachmentsTools.length,
fileOperations: fileOperationsTools.length,
oauth: oauthTools.length,
auth: authTools.length,
stars: starsTools.length,
pins: pinsTools.length,
views: viewsTools.length,
reactions: reactionsTools.length,
apiKeys: apiKeysTools.length,
webhooks: webhooksTools.length,
backlinks: backlinksTools.length,
searchQueries: searchQueriesTools.length,
teams: teamsTools.length,
integrations: integrationsTools.length,
notifications: notificationsTools.length,
subscriptions: subscriptionsTools.length,
templates: templatesTools.length,
imports: importsTools.length,
emojis: emojisTools.length,
userPermissions: userPermissionsTools.length,
bulkOperations: bulkOperationsTools.length,
advancedSearch: advancedSearchTools.length,
analytics: analyticsTools.length,
exportImport: exportImportTools.length,
deskSync: deskSyncTools.length
}
toolsByModule: getToolCounts()
});
}