import { VolumeManager } from '../managers/volume.js';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { z } from 'zod';
export function getVolumeTools() {
return [
{
name: 'docker_list_volumes',
description: 'List Docker volumes',
inputSchema: {
type: 'object',
properties: {
filters: {
type: 'object',
description: 'Filter volumes',
additionalProperties: {
type: 'array',
items: { type: 'string' },
},
},
},
},
},
{
name: 'docker_create_volume',
description: 'Create a Docker volume',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Volume name',
},
driver: {
type: 'string',
description: 'Volume driver',
},
labels: {
type: 'object',
description: 'Labels to apply to the volume',
additionalProperties: { type: 'string' },
},
},
},
},
{
name: 'docker_remove_volume',
description: 'Remove a Docker volume. Requires confirmation for safety. First call without confirm to see volume details, then call again with confirm=true to proceed. Alternatively, uses MCP Elicitation API if client supports it.',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Volume name',
},
force: {
type: 'boolean',
description: 'Force removal',
},
confirm: {
type: 'boolean',
description: 'Confirmation flag. Must be true to actually remove the volume. First call without this to see volume details.',
},
useElicitation: {
type: 'boolean',
description: 'Use MCP Elicitation API for interactive confirmation (if client supports it). Default: false (uses confirm parameter)',
},
},
required: ['name'],
},
},
{
name: 'docker_inspect_volume',
description: 'Get detailed information about a volume',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Volume name',
},
},
required: ['name'],
},
},
];
}
export async function handleVolumeTool(
name: string,
args: any,
volumeManager: VolumeManager,
server?: Server
): Promise<any> {
try {
switch (name) {
case 'docker_list_volumes': {
const parsed = z
.object({
filters: z.record(z.array(z.string())).optional(),
})
.parse(args);
const volumes = await volumeManager.listVolumes({
filters: parsed.filters,
});
return {
content: [
{
type: 'text',
text: JSON.stringify(volumes, null, 2),
},
],
};
}
case 'docker_create_volume': {
const parsed = z
.object({
name: z.string().optional(),
driver: z.string().optional(),
labels: z.record(z.string()).optional(),
})
.parse(args);
const volume = await volumeManager.createVolume({
Name: parsed.name,
Driver: parsed.driver,
Labels: parsed.labels,
});
return {
content: [
{
type: 'text',
text: JSON.stringify({ name: volume.name, message: 'Volume created successfully' }, null, 2),
},
],
};
}
case 'docker_remove_volume': {
const parsed = z
.object({
name: z.string(),
force: z.boolean().optional(),
confirm: z.boolean().optional(),
useElicitation: z.boolean().optional(),
})
.parse(args);
// Get volume info first
let volumeInfo: any;
try {
volumeInfo = await volumeManager.inspectVolume(parsed.name);
} catch (error: any) {
return {
content: [
{
type: 'text',
text: JSON.stringify({
error: `Volume not found: ${parsed.name}`,
message: error.message,
}, null, 2),
},
],
isError: true,
};
}
// Try MCP Elicitation API if requested and server is available
if (parsed.useElicitation && server) {
try {
const result = await server.elicitInput({
mode: 'form',
message: `⚠️ WARNING: You are about to remove volume "${parsed.name}". This action cannot be undone.\n\nVolume Details:\n- Name: ${volumeInfo.Name || parsed.name}\n- Driver: ${volumeInfo.Driver || 'local'}\n- Mountpoint: ${volumeInfo.Mountpoint || 'N/A'}\n\nAre you sure you want to proceed?`,
requestedSchema: {
type: 'object',
properties: {
confirm: {
type: 'boolean',
title: 'Confirm Removal',
description: 'Check this box to confirm you want to remove this volume',
},
},
required: ['confirm'],
},
});
if (result.action === 'accept' && result.content?.confirm === true) {
await volumeManager.removeVolume(parsed.name, { force: parsed.force });
return {
content: [
{
type: 'text',
text: JSON.stringify({
message: '✅ Volume removed successfully',
removed: parsed.name,
}, null, 2),
},
],
};
} else {
return {
content: [
{
type: 'text',
text: JSON.stringify({
message: '❌ Volume removal cancelled by user',
}, null, 2),
},
],
};
}
} catch (error: any) {
// Fallback to confirm parameter if elicitation fails
console.error('[Elicitation failed, falling back to confirm parameter]', error.message);
}
}
// Fallback: Use confirm parameter approach
if (!parsed.confirm) {
return {
content: [
{
type: 'text',
text: JSON.stringify({
warning: '⚠️ CONFIRMATION REQUIRED: Volume removal is a destructive operation',
volume: {
name: volumeInfo.Name || parsed.name,
driver: volumeInfo.Driver,
mountpoint: volumeInfo.Mountpoint,
labels: volumeInfo.Labels,
created: (volumeInfo as any).CreatedAt,
},
message: 'To proceed with removal, call this tool again with confirm=true',
note: 'Alternatively, you can use useElicitation=true if your MCP client supports Elicitation API',
example: {
name: 'docker_remove_volume',
arguments: {
name: parsed.name,
force: parsed.force || false,
confirm: true,
},
},
}, null, 2),
},
],
};
}
// Second call with confirm=true: Actually remove the volume
if (parsed.confirm === true) {
await volumeManager.removeVolume(parsed.name, { force: parsed.force });
return {
content: [
{
type: 'text',
text: JSON.stringify({
message: '✅ Volume removed successfully',
removed: parsed.name,
}, null, 2),
},
],
};
}
return {
content: [
{
type: 'text',
text: JSON.stringify({
error: 'Invalid confirmation state',
message: 'Please set confirm=true to proceed with volume removal',
}, null, 2),
},
],
isError: true,
};
}
case 'docker_inspect_volume': {
const parsed = z.object({ name: z.string() }).parse(args);
const info = await volumeManager.inspectVolume(parsed.name);
return {
content: [
{
type: 'text',
text: JSON.stringify(info, null, 2),
},
],
};
}
default:
return null;
}
} catch (error: any) {
if (error.message?.includes('Unknown tool')) {
return null;
}
throw error;
}
}