Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 54 additions & 2 deletions docs/dockashell-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 .",
Expand Down
30 changes: 14 additions & 16 deletions src/mcp/remote/remote-mcp-server.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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'],
Expand Down Expand Up @@ -167,7 +167,7 @@ export class RemoteMCPServer {
</style>
</head>
<body>
<h2>🐳 DockaShell Authentication</h2>
<h2>DockaShell Authentication</h2>
<form onsubmit="handleLogin(event)">
<div class="form-group">
<input type="text" id="username" placeholder="Username" required>
Expand Down Expand Up @@ -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`
);
});

Expand Down
3 changes: 3 additions & 0 deletions src/mcp/tools/execution-tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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'),
Expand Down
2 changes: 2 additions & 0 deletions src/mcp/tools/log-tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand All @@ -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
Expand Down
56 changes: 35 additions & 21 deletions src/mcp/tools/project-tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
Expand Down
14 changes: 12 additions & 2 deletions test/mcp/execution-tools.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}
},
};
}
Expand Down
14 changes: 12 additions & 2 deletions test/mcp/log-tools.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}
},
};
}
Expand Down
14 changes: 12 additions & 2 deletions test/mcp/project-tools.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}
},
};
}
Expand Down