Files
mcpctl/src/mcpd/src/services/template.service.ts
Michal d58e6e153f
Some checks failed
CI / lint (pull_request) Has been cancelled
CI / typecheck (pull_request) Has been cancelled
CI / test (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
CI / package (pull_request) Has been cancelled
feat: add MCP server templates and deployment infrastructure
Introduce a Helm-chart-like template system for MCP servers. Templates are
YAML files in templates/ that get seeded into the DB on startup. Users can
browse them with `mcpctl get templates`, inspect with `mcpctl describe
template`, and instantiate with `mcpctl create server --from-template=`.

Also adds Portainer deployment scripts, mcplocal systemd service,
Streamable HTTP MCP endpoint, and RPM packaging for mcpctl-local.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 22:24:35 +00:00

54 lines
1.6 KiB
TypeScript

import type { McpTemplate } from '@prisma/client';
import type { ITemplateRepository } from '../repositories/template.repository.js';
import { CreateTemplateSchema, UpdateTemplateSchema } from '../validation/template.schema.js';
import { NotFoundError, ConflictError } from './mcp-server.service.js';
export class TemplateService {
constructor(private readonly repo: ITemplateRepository) {}
async list(namePattern?: string): Promise<McpTemplate[]> {
if (namePattern) {
return this.repo.search(namePattern);
}
return this.repo.findAll();
}
async getById(id: string): Promise<McpTemplate> {
const template = await this.repo.findById(id);
if (template === null) {
throw new NotFoundError(`Template not found: ${id}`);
}
return template;
}
async getByName(name: string): Promise<McpTemplate> {
const template = await this.repo.findByName(name);
if (template === null) {
throw new NotFoundError(`Template not found: ${name}`);
}
return template;
}
async create(input: unknown): Promise<McpTemplate> {
const data = CreateTemplateSchema.parse(input);
const existing = await this.repo.findByName(data.name);
if (existing !== null) {
throw new ConflictError(`Template already exists: ${data.name}`);
}
return this.repo.create(data);
}
async update(id: string, input: unknown): Promise<McpTemplate> {
const data = UpdateTemplateSchema.parse(input);
await this.getById(id);
return this.repo.update(id, data);
}
async delete(id: string): Promise<void> {
await this.getById(id);
await this.repo.delete(id);
}
}