diff --git a/docs/dockashell-configuration.md b/docs/dockashell-configuration.md index 52ec29a..d3f66b6 100644 --- a/docs/dockashell-configuration.md +++ b/docs/dockashell-configuration.md @@ -6,21 +6,73 @@ This document covers both global and project-specific configuration for DockaShe DockaShell stores global settings in `~/.dockashell/config.json`. This file controls system-wide behavior and default settings. -### Global Configuration Example +### Complete Global Configuration Example ```json { + "projects": { + "directory": "~/dockashell-projects" + }, + "tui": { + "display": { + "max_entries": 300 + } + }, "logging": { "traces": { "session_timeout": "4h" } + }, + "remote_mcp": { + "enabled": false, + "port": 3333, + "auth": { + "username": "admin", + "password": "changeme123" + }, + "cors": { + "origin": "*", + "credentials": true + }, + "session": { + "timeout": "24h" + } } } ``` ### Global Configuration Fields -- `logging.traces.session_timeout` – Time between trace entries before starting a new session (e.g., `"2h"`, `"4h"`) +#### Projects Configuration + +- `projects.directory` – Default directory where project files are stored (default: `"~/dockashell-projects"`) + +#### TUI Configuration + +- `tui.display.max_entries` – Maximum number of trace entries to display in the TUI log viewer (default: `300`) + +#### Logging Configuration + +- `logging.traces.session_timeout` – Time between trace entries before starting a new session (default: `"4h"`, formats: `"2h"`, `"30m"`) + +#### Remote MCP Server Configuration + +- `remote_mcp.enabled` – Enable remote MCP server for multi-user access (default: `false`) +- `remote_mcp.port` – Port for remote MCP server (default: `3333`) + +##### Authentication Settings + +- `remote_mcp.auth.username` – Username for authentication (default: `"admin"`) +- `remote_mcp.auth.password` – Password for authentication (default: `"changeme123"` - should be changed) + +##### CORS Settings + +- `remote_mcp.cors.origin` – Allowed origins for CORS requests (default: `"*"`) +- `remote_mcp.cors.credentials` – Allow credentials in CORS requests (default: `true`) + +##### Session Settings + +- `remote_mcp.session.timeout` – Session timeout for authenticated users (default: `"24h"`) ## Project Configuration diff --git a/package.json b/package.json index 9431401..2713ac1 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "dev:tui": "node src/tui/tui-launcher.js", "start": "node src/mcp/mcp-server.js", "start:remote": "node src/cli/cli.js remote", + "debug": "npx @modelcontextprotocol/inspector node src/mcp/mcp-server.js", "test": "node --test test/**/*.test.js", "test:integration": "node test/integration/test-*.js", "lint": "eslint .", diff --git a/src/mcp/remote/remote-mcp-server.js b/src/mcp/remote/remote-mcp-server.js index 4b4c529..89de4b1 100644 --- a/src/mcp/remote/remote-mcp-server.js +++ b/src/mcp/remote/remote-mcp-server.js @@ -1,16 +1,16 @@ -import express from 'express'; -import cors from 'cors'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { SimpleAuth } from './auth/simple-auth.js'; -import { TransportManager } from './transport/transport-manager.js'; -import { registerProjectTools } from '../tools/project-tools.js'; -import { registerExecutionTools } from '../tools/execution-tools.js'; -import { registerLogTools } from '../tools/log-tools.js'; -import ProjectManager from '../../core/project-manager.js'; +import cors from 'cors'; +import express from 'express'; +import { v4 as uuidv4 } from 'uuid'; import ContainerManager from '../../core/container-manager.js'; +import ProjectManager from '../../core/project-manager.js'; import SecurityManager from '../../core/security.js'; import Logger from '../../utils/logger.js'; -import { v4 as uuidv4 } from 'uuid'; +import { registerExecutionTools } from '../tools/execution-tools.js'; +import { registerLogTools } from '../tools/log-tools.js'; +import { registerProjectTools } from '../tools/project-tools.js'; +import { SimpleAuth } from './auth/simple-auth.js'; +import { TransportManager } from './transport/transport-manager.js'; /** * Remote MCP Server with simple single-user authentication @@ -80,7 +80,7 @@ export class RemoteMCPServer { if (process.env.NODE_ENV !== 'production') { this.app.use((req, _res, next) => { if (req.method === 'OPTIONS') { - console.log('🔍 CORS Preflight Request:', { + console.log('CORS Preflight Request:', { origin: req.headers.origin, method: req.headers['access-control-request-method'], headers: req.headers['access-control-request-headers'], @@ -167,7 +167,7 @@ export class RemoteMCPServer { -

🐳 DockaShell Authentication

+

DockaShell Authentication

@@ -472,13 +472,11 @@ export class RemoteMCPServer { const server = this.app.listen(this.config.port, () => { console.log( - `🐳 DockaShell Remote MCP Server started on port ${this.config.port}` - ); - console.log( - `📊 Health check: http://localhost:${this.config.port}/health` + `DockaShell Remote MCP Server started on port ${this.config.port}` ); + console.log(`Health check: http://localhost:${this.config.port}/health`); console.log( - `🔐 OAuth discovery: http://localhost:${this.config.port}/.well-known/oauth-authorization-server` + `OAuth discovery: http://localhost:${this.config.port}/.well-known/oauth-authorization-server` ); }); diff --git a/src/mcp/tools/execution-tools.js b/src/mcp/tools/execution-tools.js index 60488bb..57106d2 100644 --- a/src/mcp/tools/execution-tools.js +++ b/src/mcp/tools/execution-tools.js @@ -10,6 +10,7 @@ export function registerExecutionTools( // Run command server.tool( 'bash', + 'Executes shell commands in a project container. Avoid interactive commands (vim, nano, less, top) as they require TTY. Use non-interactive alternatives instead.', { project_name: z.string().describe('Name of the project'), command: z.string().describe('Shell command to execute'), @@ -55,6 +56,7 @@ export function registerExecutionTools( // Apply patch server.tool( 'apply_patch', + 'Applies code patches using OpenAI format. Patch must start with "*** Begin Patch\\n" and end with "\\n*** End Patch". Use "*** Add File: path", "*** Update File: path", or "*** Delete File: path" followed by diff content with +/- lines.', { project_name: z.string().describe('Name of the project'), patch: z @@ -90,6 +92,7 @@ export function registerExecutionTools( // Write file server.tool( 'write_file', + 'Creates or overwrites files in a project container with the specified content', { project_name: z.string().describe('Name of the project'), path: z.string().describe('File path inside container'), diff --git a/src/mcp/tools/log-tools.js b/src/mcp/tools/log-tools.js index 73f6719..65bd298 100644 --- a/src/mcp/tools/log-tools.js +++ b/src/mcp/tools/log-tools.js @@ -5,6 +5,7 @@ export function registerLogTools(server, logger) { // Write trace note server.tool( 'write_trace', + 'Records trace entries for auditing agent actions and preserving session context', { project_name: z.string().describe('Project name'), type: z.enum(['user', 'summary', 'agent']).describe('Note type'), @@ -23,6 +24,7 @@ export function registerLogTools(server, logger) { // Read traces server.tool( 'read_traces', + 'Retrieves and filters trace history for a project, showing command executions, file operations, and notes with timestamps', { project_name: z.string().describe('Project name'), type: z diff --git a/src/mcp/tools/project-tools.js b/src/mcp/tools/project-tools.js index 51358db..58eab66 100644 --- a/src/mcp/tools/project-tools.js +++ b/src/mcp/tools/project-tools.js @@ -3,31 +3,39 @@ import { validateProjectName, textResponse } from './helpers.js'; export function registerProjectTools(server, projectManager, containerManager) { // List projects - server.tool('list_projects', {}, async () => { - try { - const projects = await projectManager.listProjects(); - let response = '# Configured Projects\n\n'; - if (projects.length === 0) { - response += - 'No projects configured. Create project configs in `~/.dockashell/projects/`\n'; - } else { - projects.forEach((project) => { - response += `**${project.name}**\n`; - response += `- Description: ${project.description || 'None'}\n`; - response += `- Image: ${project.image}\n`; - response += `- Status: ${project.status}\n\n`; - }); + server.tool( + 'list_projects', + 'Lists all configured DockaShell projects with their status and configuration details', + {}, + async () => { + try { + const projects = await projectManager.listProjects(); + let response = '# Configured Projects\n\n'; + if (projects.length === 0) { + response += + 'No projects configured. Create project configs in `~/.dockashell/projects/`\n'; + } else { + projects.forEach((project) => { + response += `**${project.name}**\n`; + response += `- Description: ${project.description || 'None'}\n`; + response += `- Image: ${project.image}\n`; + response += `- Status: ${project.status}\n\n`; + }); + } + return textResponse(response); + } catch (error) { + throw new Error(`Failed to list projects: ${error.message}`); } - return textResponse(response); - } catch (error) { - throw new Error(`Failed to list projects: ${error.message}`); } - }); + ); // Start project server.tool( 'start_project', - { project_name: z.string().describe('Name of the project to start') }, + 'Starts a Docker container for the specified project with configured mounts and port forwarding', + { + project_name: z.string().describe('Name of the project to start'), + }, async ({ project_name }) => { validateProjectName(project_name); try { @@ -66,7 +74,10 @@ export function registerProjectTools(server, projectManager, containerManager) { // Stop project server.tool( 'stop_project', - { project_name: z.string().describe('Name of the project to stop') }, + 'Stops the running Docker container for the specified project', + { + project_name: z.string().describe('Name of the project to stop'), + }, async ({ project_name }) => { validateProjectName(project_name); try { @@ -86,7 +97,10 @@ export function registerProjectTools(server, projectManager, containerManager) { // Project status server.tool( 'project_status', - { project_name: z.string().describe('Name of the project to check') }, + 'Shows current status, configuration, and runtime details of a project container', + { + project_name: z.string().describe('Name of the project to check'), + }, async ({ project_name }) => { try { validateProjectName(project_name); diff --git a/test/mcp/execution-tools.test.js b/test/mcp/execution-tools.test.js index 3efaab3..118ed6d 100644 --- a/test/mcp/execution-tools.test.js +++ b/test/mcp/execution-tools.test.js @@ -5,8 +5,18 @@ import { registerExecutionTools } from '../../src/mcp/tools/execution-tools.js'; function createServer() { return { tools: {}, - tool(name, _schema, handler) { - this.tools[name] = { handler }; + tool(name, description, schema, handler) { + // Handle both 3-param (old) and 4-param (new) formats + if (typeof description === 'function') { + // 3-param format: tool(name, schema, handler) + this.tools[name] = { handler: description }; + } else if (typeof schema === 'function') { + // 4-param format: tool(name, description, schema, handler) + this.tools[name] = { handler: schema }; + } else { + // 4-param format: tool(name, description, schema, handler) + this.tools[name] = { handler }; + } }, }; } diff --git a/test/mcp/log-tools.test.js b/test/mcp/log-tools.test.js index cc67d4b..767ef5d 100644 --- a/test/mcp/log-tools.test.js +++ b/test/mcp/log-tools.test.js @@ -5,8 +5,18 @@ import { registerLogTools } from '../../src/mcp/tools/log-tools.js'; function createServer() { return { tools: {}, - tool(name, _schema, handler) { - this.tools[name] = { handler }; + tool(name, description, schema, handler) { + // Handle both 3-param (old) and 4-param (new) formats + if (typeof description === 'function') { + // 3-param format: tool(name, schema, handler) + this.tools[name] = { handler: description }; + } else if (typeof schema === 'function') { + // 4-param format: tool(name, description, schema, handler) + this.tools[name] = { handler: schema }; + } else { + // 4-param format: tool(name, description, schema, handler) + this.tools[name] = { handler }; + } }, }; } diff --git a/test/mcp/project-tools.test.js b/test/mcp/project-tools.test.js index 0058ad4..3ae0fd6 100644 --- a/test/mcp/project-tools.test.js +++ b/test/mcp/project-tools.test.js @@ -5,8 +5,18 @@ import { registerProjectTools } from '../../src/mcp/tools/project-tools.js'; function createServer() { return { tools: {}, - tool(name, _schema, handler) { - this.tools[name] = { handler }; + tool(name, description, schema, handler) { + // Handle both 3-param (old) and 4-param (new) formats + if (typeof description === 'function') { + // 3-param format: tool(name, schema, handler) + this.tools[name] = { handler: description }; + } else if (typeof schema === 'function') { + // 4-param format: tool(name, description, schema, handler) + this.tools[name] = { handler: schema }; + } else { + // 4-param format: tool(name, description, schema, handler) + this.tools[name] = { handler }; + } }, }; }