import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import type {
DomainMeta,
DomainResource,
} from "../../shared/types/domain.types.js";
import { formatErrorForMcpTool } from "../../shared/utils/error.util.js";
import { Logger } from "../../shared/utils/logger.util.js";
import {{domainName}}Controller from "./{{domainName}}.controller.js";
/**
* {{domainNamePascal}} MCP resources implementation.
* Generated on {{date}} for {{domainDescription}}.
*/
const logger = Logger.forContext("domains/{{domainName}}/{{domainName}}.resource.ts");
{{#if:collection}}
/**
* Handle {{domainName}} collection resource requests
*/
async function handle{{domainNamePascal}}CollectionResource(uri: URL): Promise<any> {
const methodLogger = logger.forMethod("handle{{domainNamePascal}}CollectionResource");
methodLogger.debug("Handling {{domainName}} collection resource request", { uri: uri.toString() });
try {
// Extract project ID from path
const pathParts = uri.pathname.split("/").filter(Boolean);
if (pathParts.length < 2 || pathParts[0] !== "{{domainName}}") {
throw new Error("Invalid resource URI format. Expected: lokalise://{{domainName}}/<project-id>");
}
const projectId = pathParts[1];
// Extract query parameters
const limit = uri.searchParams.get("limit");
const page = uri.searchParams.get("page");
const filterQuery = uri.searchParams.get("filter");
// TODO: Add more domain-specific filters
// Build arguments
const args = {
projectId,
limit: limit ? Number.parseInt(limit, 10) : undefined,
page: page ? Number.parseInt(page, 10) : undefined,
filterQuery: filterQuery || undefined,
};
// Call controller
const result = await {{domainName}}Controller.list{{domainNamePascal}}(args);
return {
contents: [
{
uri: uri.toString(),
mimeType: "text/markdown",
text: result.content,
},
],
};
} catch (error) {
methodLogger.error("Resource handler failed", { error: (error as Error).message });
throw error;
}
}
{{/if:collection}}
{{#if:detail}}
/**
* Handle {{domainName}} detail resource requests
*/
async function handle{{domainNamePascal}}DetailResource(uri: URL): Promise<any> {
const methodLogger = logger.forMethod("handle{{domainNamePascal}}DetailResource");
methodLogger.debug("Handling {{domainName}} detail resource request", { uri: uri.toString() });
try {
// Extract project ID and {{domainName}} ID from path
const pathParts = uri.pathname.split("/").filter(Boolean);
if (pathParts.length < 3 || pathParts[0] !== "{{domainName}}") {
throw new Error("Invalid resource URI format. Expected: lokalise://{{domainName}}/<project-id>/<{{domainName}}-id>");
}
const projectId = pathParts[1];
const {{domainName}}Id = pathParts[2];
// Build arguments
const args = {
projectId,
{{domainName}}Id: Number.parseInt({{domainName}}Id, 10),
};
// Call controller
const result = await {{domainName}}Controller.get{{domainNamePascal}}(args);
return {
contents: [
{
uri: uri.toString(),
mimeType: "text/markdown",
text: result.content,
},
],
};
} catch (error) {
methodLogger.error("Resource handler failed", { error: (error as Error).message });
throw error;
}
}
{{/if:detail}}
/**
* Main resource request handler
*/
async function handleResourceRequest(uri: string): Promise<any> {
const methodLogger = logger.forMethod("handleResourceRequest");
methodLogger.debug("Processing {{domainName}} resource request", { uri });
try {
const parsedUri = new URL(uri);
// Route to appropriate handler based on URI structure
const pathParts = parsedUri.pathname.split("/").filter(Boolean);
if (pathParts.length === 0) {
throw new Error("Invalid resource URI. Expected lokalise://{{domainName}}/<project-id> or lokalise://{{domainName}}/<project-id>/<{{domainName}}-id>");
}
{{#if:detail}}
// Check if this is a detail request (has {{domainName}} ID)
if (pathParts.length >= 3) {
return await handle{{domainNamePascal}}DetailResource(parsedUri);
}
{{/if:detail}}
{{#if:collection}}
// Otherwise, it's a collection request
return await handle{{domainNamePascal}}CollectionResource(parsedUri);
{{/if:collection}}
// If no handlers matched
throw new Error("No handler available for this resource URI pattern");
} catch (error) {
methodLogger.error("Failed to handle resource request", { error: (error as Error).message, uri });
return formatErrorForMcpTool(error);
}
}
/**
* Register all MCP resources for the {{domainName}} domain
* @param server The MCP server instance to register resources with
*/
function registerResources(server: McpServer): void {
const registerLogger = Logger.forContext("{{domainName}}.resource.ts", "registerResources");
registerLogger.debug("Registering {{domainName}} domain resources...");
{{#if:collection}}
// Register collection resource
server.resource(
"lokalise-{{domainNameKebab}}",
new ResourceTemplate("lokalise://{{domainName}}/{projectId}", {
list: undefined, // No listing of resources needed
}),
async (uri: URL) => handle{{domainNamePascal}}CollectionResource(uri),
);
{{/if:collection}}
{{#if:detail}}
// Register detail resource
server.resource(
"lokalise-{{domainNameKebab}}-details",
new ResourceTemplate(
"lokalise://{{domainName}}/{projectId}/{{{domainName}}Id}",
{
list: undefined, // No listing of resources needed
},
),
async (uri: URL) => handle{{domainNamePascal}}DetailResource(uri),
);
{{/if:detail}}
registerLogger.debug("{{domainNamePascal}} domain resources registered successfully");
}
/**
* Get metadata about the {{domainName}} domain resources
*/
function getMeta(): DomainMeta {
return {
name: "{{domainName}}",
description: "{{domainDescription}} MCP resources",
version: "1.0.0",
resourcesCount: {{resourcesCount}},
};
}
const {{domainName}}Resource: DomainResource = {
registerResources,
getMeta,
};
export default {{domainName}}Resource;