import { ImageManager } from '../managers/image.js';
import { z } from 'zod';
export function getImageTools() {
return [
{
name: 'docker_list_images',
description: 'List Docker images',
inputSchema: {
type: 'object',
properties: {
all: {
type: 'boolean',
description: 'Show all images (including intermediate images)',
},
filters: {
type: 'object',
description: 'Filter images',
additionalProperties: {
type: 'array',
items: { type: 'string' },
},
},
},
},
},
{
name: 'docker_pull_image',
description: 'Pull an image from a registry',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Image name (e.g., "nginx:latest", "ubuntu:20.04")',
},
username: {
type: 'string',
description: 'Registry username (for private images)',
},
password: {
type: 'string',
description: 'Registry password or token',
},
},
required: ['name'],
},
},
{
name: 'docker_push_image',
description: 'Push an image to a registry',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Image name to push',
},
username: {
type: 'string',
description: 'Registry username',
},
password: {
type: 'string',
description: 'Registry password or token',
},
},
required: ['name'],
},
},
{
name: 'docker_tag_image',
description: 'Tag an image',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Source image name',
},
repo: {
type: 'string',
description: 'Target repository name',
},
tag: {
type: 'string',
description: 'Tag name (default: "latest")',
},
},
required: ['name', 'repo'],
},
},
{
name: 'docker_remove_image',
description: 'Remove an image. Requires confirmation for safety. First call without confirm to see image details, then call again with confirm=true to proceed.',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Image name or ID',
},
force: {
type: 'boolean',
description: 'Force removal of the image',
},
confirm: {
type: 'boolean',
description: 'Confirmation flag. Must be true to actually remove the image. First call without this to see image details.',
},
},
required: ['name'],
},
},
{
name: 'docker_inspect_image',
description: 'Get detailed information about an image',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Image name or ID',
},
},
required: ['name'],
},
},
{
name: 'docker_image_history',
description: 'Get the history of an image',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Image name or ID',
},
},
required: ['name'],
},
},
];
}
export async function handleImageTool(
name: string,
args: any,
imageManager: ImageManager
): Promise<any> {
try {
switch (name) {
case 'docker_list_images': {
const parsed = z
.object({
all: z.boolean().optional(),
filters: z.record(z.array(z.string())).optional(),
})
.parse(args);
const images = await imageManager.listImages({
all: parsed.all,
filters: parsed.filters,
});
return {
content: [
{
type: 'text',
text: JSON.stringify(images, null, 2),
},
],
};
}
case 'docker_pull_image': {
const parsed = z
.object({
name: z.string(),
username: z.string().optional(),
password: z.string().optional(),
})
.parse(args);
const authconfig = parsed.username && parsed.password
? {
username: parsed.username,
password: parsed.password,
}
: undefined;
const stream = await imageManager.pullImage(parsed.name, { authconfig });
// Read stream to completion
const chunks: Buffer[] = [];
for await (const chunk of stream) {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
}
return {
content: [
{
type: 'text',
text: JSON.stringify({ message: 'Image pulled successfully' }, null, 2),
},
],
};
}
case 'docker_push_image': {
const parsed = z
.object({
name: z.string(),
username: z.string().optional(),
password: z.string().optional(),
})
.parse(args);
const authconfig = parsed.username && parsed.password
? {
username: parsed.username,
password: parsed.password,
}
: undefined;
const stream = await imageManager.pushImage(parsed.name, { authconfig });
const chunks: Buffer[] = [];
for await (const chunk of stream) {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
}
return {
content: [
{
type: 'text',
text: JSON.stringify({ message: 'Image pushed successfully' }, null, 2),
},
],
};
}
case 'docker_tag_image': {
const parsed = z
.object({
name: z.string(),
repo: z.string(),
tag: z.string().optional(),
})
.parse(args);
await imageManager.tagImage(parsed.name, parsed.repo, parsed.tag);
return {
content: [
{
type: 'text',
text: JSON.stringify({ message: 'Image tagged successfully' }, null, 2),
},
],
};
}
case 'docker_remove_image': {
const parsed = z
.object({
name: z.string(),
force: z.boolean().optional(),
confirm: z.boolean().optional(),
})
.parse(args);
// First call: Show image details and request confirmation
if (!parsed.confirm) {
try {
const imageInfo = await imageManager.inspectImage(parsed.name);
return {
content: [
{
type: 'text',
text: JSON.stringify({
warning: '⚠️ CONFIRMATION REQUIRED: Image removal is a destructive operation',
image: {
id: imageInfo.Id,
repoTags: imageInfo.RepoTags || [],
size: imageInfo.Size,
created: imageInfo.Created,
architecture: imageInfo.Architecture,
os: imageInfo.Os,
},
message: 'To proceed with removal, call this tool again with confirm=true',
example: {
name: 'docker_remove_image',
arguments: {
name: parsed.name,
force: parsed.force || false,
confirm: true,
},
},
}, null, 2),
},
],
};
} catch (error: any) {
return {
content: [
{
type: 'text',
text: JSON.stringify({
error: `Image not found: ${parsed.name}`,
message: error.message,
}, null, 2),
},
],
isError: true,
};
}
}
// Second call with confirm=true: Actually remove the image
if (parsed.confirm === true) {
await imageManager.removeImage(parsed.name, { force: parsed.force });
return {
content: [
{
type: 'text',
text: JSON.stringify({
message: '✅ Image 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 image removal',
}, null, 2),
},
],
isError: true,
};
}
case 'docker_inspect_image': {
const parsed = z.object({ name: z.string() }).parse(args);
const info = await imageManager.inspectImage(parsed.name);
return {
content: [
{
type: 'text',
text: JSON.stringify(info, null, 2),
},
],
};
}
case 'docker_image_history': {
const parsed = z.object({ name: z.string() }).parse(args);
const history = await imageManager.getImageHistory(parsed.name);
return {
content: [
{
type: 'text',
text: JSON.stringify(history, null, 2),
},
],
};
}
default:
return null; // Let other handlers try
}
} catch (error: any) {
if (error.message?.includes('Unknown tool')) {
return null;
}
throw error;
}
}