feat: initial commit - Jhonny Editor
- Adicionado estrutura completa do projeto - Configurado MCP server para Premiere Pro - Adicionado documentação e skills - Configurado Gitignore para o projeto
This commit is contained in:
Executable
+576
@@ -0,0 +1,576 @@
|
||||
import {
|
||||
fromJsonSchema,
|
||||
McpServer,
|
||||
type JsonSchemaType,
|
||||
type StandardSchemaWithJSON,
|
||||
} from "@modelcontextprotocol/server";
|
||||
import { BridgeOptions } from "./bridge/file-bridge.js";
|
||||
import { getDiscoveryTools } from "./tools/discovery.js";
|
||||
import { getProjectTools } from "./tools/project.js";
|
||||
import { getMediaTools } from "./tools/media.js";
|
||||
import { getSequenceTools } from "./tools/sequence.js";
|
||||
import { getTimelineTools } from "./tools/timeline.js";
|
||||
import { getEffectsTools } from "./tools/effects.js";
|
||||
import { getTransitionsTools } from "./tools/transitions.js";
|
||||
import { getAudioTools } from "./tools/audio.js";
|
||||
import { getTextTools } from "./tools/text.js";
|
||||
import { getMarkerTools } from "./tools/markers.js";
|
||||
import { getTrackTools } from "./tools/tracks.js";
|
||||
import { getPlayheadTools } from "./tools/playhead.js";
|
||||
import { getMetadataTools } from "./tools/metadata.js";
|
||||
import { getExportTools } from "./tools/export.js";
|
||||
import { getMediaAnalysisTools } from "./tools/media-analysis.js";
|
||||
import { getInterchangeAnalysisTools } from "./tools/interchange-analysis.js";
|
||||
import { getAdvancedTools } from "./tools/advanced.js";
|
||||
import { getKeyframeTools } from "./tools/keyframes.js";
|
||||
import { getScriptingTools } from "./tools/scripting.js";
|
||||
import { getInspectionTools } from "./tools/inspection.js";
|
||||
import { getSelectionTools } from "./tools/selection.js";
|
||||
import { getClipboardTools } from "./tools/clipboard.js";
|
||||
import { getSourceMonitorTools } from "./tools/source-monitor.js";
|
||||
import { getTrackTargetingTools } from "./tools/track-targeting.js";
|
||||
import { getUtilityTools } from "./tools/utility.js";
|
||||
import { getHealthTools } from "./tools/health.js";
|
||||
import { getWorkspaceTools } from "./tools/workspace.js";
|
||||
import { getCaptionTools } from "./tools/captions.js";
|
||||
import { getPlaybackTools } from "./tools/playback.js";
|
||||
import { getProjectManagerTools } from "./tools/project-manager.js";
|
||||
import { getEditPlanTools } from "./tools/edit-plans.js";
|
||||
import { getSpotWorkflowTools } from "./tools/spot-workflows.js";
|
||||
import { getAvSettingsTools } from "./tools/av-settings.js";
|
||||
import { getRecoveryTools } from "./tools/recovery.js";
|
||||
import { getProjectContextTools } from "./tools/project-context.js";
|
||||
import { getEditorialPlanTools } from "./tools/editorial-plans.js";
|
||||
import { getEditorialContextPackTools } from "./tools/editorial-context-pack.js";
|
||||
import { ProjectContextRepository } from "./context/project-context-store.js";
|
||||
import { getProjectIntakeTools } from "./tools/project-intake.js";
|
||||
import { getCompetitorGapTools } from "./tools/competitor-gaps.js";
|
||||
import { getDialogueAnalysisTools } from "./tools/dialogue-analysis.js";
|
||||
import { MediaWatchRegistry, getMediaWatchTools } from "./tools/media-watch.js";
|
||||
import { getWorkflowRecipeTools } from "./tools/workflow-recipes.js";
|
||||
import { getUxpTools } from "./tools/uxp.js";
|
||||
import { getMogrtAuthoringTools } from "./tools/mogrt-authoring.js";
|
||||
import { getMogrtStudioTools } from "./tools/mogrt-studio.js";
|
||||
import type { UxpWebSocketBridge } from "./bridge/uxp-websocket-bridge.js";
|
||||
import {
|
||||
guardToolHandler,
|
||||
isToolPermitted,
|
||||
resolveCapabilities,
|
||||
} from "./security/index.js";
|
||||
import { EXTENDSCRIPT_REFERENCE } from "./resources/extendscript-reference.js";
|
||||
import { getLiveContextResources } from "./resources/live-context-resources.js";
|
||||
import { PROJECT_CONTEXT_RESOURCE } from "./context/project-context-resource.js";
|
||||
import { buildPremiereInstructions } from "./workflows/agent-instructions.js";
|
||||
import { WORKFLOW_PROMPTS, WORKFLOW_RESOURCE } from "./workflows/catalog.js";
|
||||
import {
|
||||
annotationsForTool,
|
||||
structuredToolResult,
|
||||
} from "./workflows/tool-metadata.js";
|
||||
import {
|
||||
isToolInSelectedPacks,
|
||||
resolveToolPacks,
|
||||
type ToolPackSelection,
|
||||
} from "./workflows/tool-packs.js";
|
||||
import { getTelemetry, type Telemetry } from "./telemetry.js";
|
||||
import { z } from "zod";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import path from "node:path";
|
||||
|
||||
/**
|
||||
* Read the version from package.json rather than hardcoding it. A literal here
|
||||
* silently drifts from the published package every release, which makes the
|
||||
* version reported over MCP useless for triaging bug reports.
|
||||
*/
|
||||
export const SERVER_VERSION = ((): string => {
|
||||
try {
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const raw = readFileSync(path.resolve(here, "../package.json"), "utf8");
|
||||
const version: unknown = JSON.parse(raw).version;
|
||||
return typeof version === "string" && version ? version : "unknown";
|
||||
} catch {
|
||||
return "unknown";
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
interface ToolDef {
|
||||
description: string;
|
||||
parameters: Record<string, unknown>;
|
||||
handler: (
|
||||
args: any,
|
||||
) => Promise<{ success: boolean; data?: unknown; error?: string }>;
|
||||
}
|
||||
|
||||
const toolCatalogCache = new Map<string, Record<string, ToolDef>>();
|
||||
const inputSchemaCache = new WeakMap<
|
||||
Record<string, unknown>,
|
||||
StandardSchemaWithJSON<unknown, unknown>
|
||||
>();
|
||||
const inputSchemaContentCache = new Map<
|
||||
string,
|
||||
StandardSchemaWithJSON<unknown, unknown>
|
||||
>();
|
||||
const JSON_SCHEMA_DRAFT_2020_12 = "https://json-schema.org/draft/2020-12/schema";
|
||||
const toolResultOutputSchema = fromJsonSchema({
|
||||
$schema: JSON_SCHEMA_DRAFT_2020_12,
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
ok: {
|
||||
type: "boolean",
|
||||
description: "Whether the tool completed successfully.",
|
||||
},
|
||||
tool: {
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
description: "The registered MCP tool name.",
|
||||
},
|
||||
data: {
|
||||
description: "Tool-specific result data when ok is true.",
|
||||
},
|
||||
error: {
|
||||
type: "string",
|
||||
description: "Failure detail when ok is false.",
|
||||
},
|
||||
},
|
||||
required: ["ok", "tool"],
|
||||
} as JsonSchemaType);
|
||||
const debugEnabled = /^(1|true|yes|on|debug)$/i.test(
|
||||
process.env.PREMIERE_MCP_DEBUG ?? "",
|
||||
);
|
||||
|
||||
function debugLog(message: string): void {
|
||||
if (debugEnabled) {
|
||||
console.error(`[premiere-pro-mcp] ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reuse the source JSON Schema directly. The MCP SDK's adapter supplies both
|
||||
* AJV validation and the exact schema needed for tools/list, avoiding a costly
|
||||
* Zod-to-JSON-Schema conversion for every stateless HTTP request.
|
||||
*/
|
||||
function jsonSchemaToInputSchema(
|
||||
params: Record<string, unknown>,
|
||||
): StandardSchemaWithJSON<unknown, unknown> {
|
||||
const cached = inputSchemaCache.get(params);
|
||||
if (cached) return cached;
|
||||
|
||||
// Context-aware tools are intentionally re-created for each server, so their
|
||||
// parameter objects have new identities even though their JSON Schema is
|
||||
// unchanged. Reuse the compiled adapter by schema content as well.
|
||||
const cacheKey = JSON.stringify(params);
|
||||
const contentCached = inputSchemaContentCache.get(cacheKey);
|
||||
if (contentCached) {
|
||||
inputSchemaCache.set(params, contentCached);
|
||||
return contentCached;
|
||||
}
|
||||
|
||||
const sourceSchema = params.$schema === undefined
|
||||
? { $schema: JSON_SCHEMA_DRAFT_2020_12, ...params }
|
||||
: params;
|
||||
const schema = fromJsonSchema(sourceSchema as JsonSchemaType);
|
||||
inputSchemaCache.set(params, schema);
|
||||
inputSchemaContentCache.set(cacheKey, schema);
|
||||
return schema;
|
||||
}
|
||||
|
||||
function collectStaticTools(
|
||||
bridgeOptions: BridgeOptions,
|
||||
capabilities: ReturnType<typeof resolveCapabilities>,
|
||||
): Record<string, ToolDef> {
|
||||
const cacheKey = JSON.stringify({
|
||||
tempDir: bridgeOptions.tempDir ?? process.env.PREMIERE_TEMP_DIR ?? null,
|
||||
timeoutMs:
|
||||
bridgeOptions.timeoutMs ?? process.env.PREMIERE_TIMEOUT_MS ?? null,
|
||||
failFastOnUnreadyHeartbeat: bridgeOptions.failFastOnUnreadyHeartbeat ?? false,
|
||||
capabilities: [...capabilities.capabilities].sort(),
|
||||
});
|
||||
const cached = toolCatalogCache.get(cacheKey);
|
||||
if (cached) return cached;
|
||||
|
||||
const tools: Record<string, ToolDef> = {
|
||||
...getDiscoveryTools(bridgeOptions),
|
||||
...getProjectTools(bridgeOptions),
|
||||
...getMediaTools(bridgeOptions),
|
||||
...getSequenceTools(bridgeOptions),
|
||||
...getTimelineTools(bridgeOptions),
|
||||
...getEffectsTools(bridgeOptions),
|
||||
...getTransitionsTools(bridgeOptions),
|
||||
...getAudioTools(bridgeOptions),
|
||||
...getTextTools(bridgeOptions),
|
||||
...getMogrtAuthoringTools(bridgeOptions),
|
||||
...getMogrtStudioTools(bridgeOptions),
|
||||
...getMarkerTools(bridgeOptions),
|
||||
...getTrackTools(bridgeOptions),
|
||||
...getPlayheadTools(bridgeOptions),
|
||||
...getMetadataTools(bridgeOptions),
|
||||
...getExportTools(bridgeOptions),
|
||||
...getMediaAnalysisTools(bridgeOptions),
|
||||
...getInterchangeAnalysisTools(bridgeOptions),
|
||||
...getAdvancedTools(bridgeOptions),
|
||||
...getKeyframeTools(bridgeOptions),
|
||||
...getScriptingTools(bridgeOptions),
|
||||
...getInspectionTools(bridgeOptions),
|
||||
...getSelectionTools(bridgeOptions),
|
||||
...getClipboardTools(bridgeOptions),
|
||||
...getSourceMonitorTools(bridgeOptions),
|
||||
...getTrackTargetingTools(bridgeOptions),
|
||||
...getUtilityTools(bridgeOptions),
|
||||
...getWorkspaceTools(bridgeOptions),
|
||||
...getCaptionTools(bridgeOptions),
|
||||
...getPlaybackTools(bridgeOptions),
|
||||
...getProjectManagerTools(bridgeOptions),
|
||||
...getEditPlanTools(bridgeOptions, { capabilities }),
|
||||
...getSpotWorkflowTools(bridgeOptions, { capabilities }),
|
||||
...getAvSettingsTools(bridgeOptions),
|
||||
...getRecoveryTools(bridgeOptions),
|
||||
...getProjectIntakeTools(bridgeOptions),
|
||||
...getDialogueAnalysisTools(),
|
||||
...getWorkflowRecipeTools(),
|
||||
};
|
||||
toolCatalogCache.set(cacheKey, tools);
|
||||
return tools;
|
||||
}
|
||||
|
||||
function collectTools(
|
||||
bridgeOptions: BridgeOptions,
|
||||
capabilities: ReturnType<typeof resolveCapabilities>,
|
||||
uxpBridge?: UxpWebSocketBridge,
|
||||
telemetry?: Telemetry,
|
||||
toolPacks?: ToolPackSelection,
|
||||
projectContextRepository = new ProjectContextRepository(),
|
||||
mediaWatchRegistry = new MediaWatchRegistry(),
|
||||
): Record<string, ToolDef> {
|
||||
// Streamable HTTP creates an independent McpServer for every request. Most
|
||||
// tool definitions are immutable, while context, telemetry, and UXP tools
|
||||
// close over server-specific state. Reuse only the former so initialization
|
||||
// stays fast without ever leaking one request's project context or bridge.
|
||||
const tools: Record<string, ToolDef> = {
|
||||
...collectStaticTools(bridgeOptions, capabilities),
|
||||
...getProjectContextTools(bridgeOptions, { repository: projectContextRepository }),
|
||||
...getEditorialContextPackTools({ repository: projectContextRepository }),
|
||||
...getEditorialPlanTools({ repository: projectContextRepository, uxpBridge }),
|
||||
...getCompetitorGapTools(bridgeOptions, uxpBridge),
|
||||
...getMediaWatchTools(mediaWatchRegistry),
|
||||
...(uxpBridge ? getUxpTools(uxpBridge) : {}),
|
||||
};
|
||||
Object.assign(
|
||||
tools,
|
||||
getHealthTools(bridgeOptions, capabilities, () => tools, {
|
||||
telemetry,
|
||||
uxpBridge,
|
||||
toolPacks,
|
||||
}),
|
||||
);
|
||||
return tools;
|
||||
}
|
||||
|
||||
export interface ServerOptions {
|
||||
uxpBridge?: UxpWebSocketBridge;
|
||||
telemetry?: Telemetry;
|
||||
/** Allows embedded hosts to share an explicitly configured context backend. */
|
||||
contextRepository?: ProjectContextRepository;
|
||||
/** Shares session-scoped watched-folder state across request-scoped servers. */
|
||||
mediaWatchRegistry?: MediaWatchRegistry;
|
||||
/** Overrides PREMIERE_MCP_TOOL_PACKS for this server instance. */
|
||||
toolPacks?: string;
|
||||
}
|
||||
|
||||
export function createServer(
|
||||
bridgeOptions: BridgeOptions,
|
||||
serverOptions: ServerOptions = {},
|
||||
): McpServer {
|
||||
|
||||
const capabilities = resolveCapabilities();
|
||||
const toolPacks = serverOptions.toolPacks === undefined
|
||||
? resolveToolPacks()
|
||||
: resolveToolPacks(serverOptions.toolPacks, "explicit");
|
||||
const telemetry = serverOptions.telemetry ?? getTelemetry();
|
||||
// Context consumers must share a single in-memory backend as well as the
|
||||
// durable backends; independent repository instances would otherwise make
|
||||
// a just-captured memory context invisible to plans and reading packs.
|
||||
const projectContextRepository = serverOptions.contextRepository ?? new ProjectContextRepository();
|
||||
const mediaWatchRegistry = serverOptions.mediaWatchRegistry ?? new MediaWatchRegistry();
|
||||
|
||||
// Collect all tools from each module
|
||||
const toolModules = collectTools(
|
||||
bridgeOptions,
|
||||
capabilities,
|
||||
serverOptions.uxpBridge,
|
||||
telemetry,
|
||||
toolPacks,
|
||||
projectContextRepository,
|
||||
mediaWatchRegistry,
|
||||
);
|
||||
|
||||
const premiereInstructions = buildPremiereInstructions(new Set(
|
||||
Object.keys(toolModules).filter((name) =>
|
||||
isToolPermitted(name, capabilities) && isToolInSelectedPacks(name, toolPacks)),
|
||||
));
|
||||
const server = new McpServer(
|
||||
{
|
||||
name: "premiere-pro-mcp",
|
||||
version: SERVER_VERSION,
|
||||
},
|
||||
{
|
||||
instructions: premiereInstructions,
|
||||
cacheHints: {
|
||||
"tools/list": { ttlMs: 30_000, cacheScope: "private" },
|
||||
"prompts/list": { ttlMs: 300_000, cacheScope: "public" },
|
||||
"resources/list": { ttlMs: 60_000, cacheScope: "private" },
|
||||
"resources/read": { ttlMs: 0, cacheScope: "private" },
|
||||
},
|
||||
capabilities: {
|
||||
extensions: {
|
||||
"io.github.leancoderkavy/premiere-pro": {
|
||||
protocolRevision: "2026-07-28",
|
||||
dualEra: true,
|
||||
transports: ["stdio", "streamable-http"],
|
||||
bridgeBackends: ["cep", "uxp"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Register each tool with the MCP server
|
||||
let authorityWithheld = 0;
|
||||
let packWithheld = 0;
|
||||
for (const [name, tool] of Object.entries(toolModules)) {
|
||||
// Don't advertise tools the active authority profile will always refuse.
|
||||
// guardToolHandler still rejects them at call time, but listing an
|
||||
// unusable tool spends client context and invites the model to attempt a
|
||||
// call that cannot succeed.
|
||||
if (!isToolPermitted(name, capabilities)) {
|
||||
authorityWithheld++;
|
||||
continue;
|
||||
}
|
||||
// Tool packs are a discovery/context optimization. They only decide which
|
||||
// permitted tools this server registers; they never change the capability
|
||||
// guard that protects every registered handler.
|
||||
if (!isToolInSelectedPacks(name, toolPacks)) {
|
||||
packWithheld++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const inputSchema = jsonSchemaToInputSchema(tool.parameters);
|
||||
const guardedHandler = guardToolHandler(name, tool.handler, capabilities);
|
||||
|
||||
const annotations = annotationsForTool(name);
|
||||
server.registerTool(
|
||||
name,
|
||||
{
|
||||
title: annotations.title,
|
||||
description: tool.description,
|
||||
inputSchema,
|
||||
outputSchema: toolResultOutputSchema,
|
||||
annotations,
|
||||
},
|
||||
async (args: unknown) => {
|
||||
const startedAt = Date.now();
|
||||
try {
|
||||
const result = await guardedHandler(args as Record<string, unknown>);
|
||||
telemetry.capture("mcp_tool_call", {
|
||||
tool: name,
|
||||
outcome: result.success ? "succeeded" : "failed",
|
||||
duration_ms: Date.now() - startedAt,
|
||||
});
|
||||
if (result.success) {
|
||||
// Special handling for capture_frame: return image content block
|
||||
const data = result.data as Record<string, unknown> | undefined;
|
||||
if (
|
||||
data &&
|
||||
data.mimeType === "image/png" &&
|
||||
typeof data.base64 === "string"
|
||||
) {
|
||||
return {
|
||||
structuredContent: structuredToolResult(name, true, {
|
||||
mimeType: data.mimeType,
|
||||
}),
|
||||
content: [
|
||||
{
|
||||
type: "image" as const,
|
||||
data: data.base64 as string,
|
||||
mimeType: "image/png" as const,
|
||||
},
|
||||
{
|
||||
type: "text" as const,
|
||||
text: "Frame captured successfully.",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
return {
|
||||
structuredContent: structuredToolResult(name, true, result.data),
|
||||
content: [
|
||||
{
|
||||
type: "text" as const,
|
||||
text: JSON.stringify(result.data, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
structuredContent: structuredToolResult(
|
||||
name,
|
||||
false,
|
||||
undefined,
|
||||
result.error,
|
||||
),
|
||||
content: [
|
||||
{
|
||||
type: "text" as const,
|
||||
text: `Error: ${result.error}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
telemetry.capture("mcp_tool_call", {
|
||||
tool: name,
|
||||
outcome: "failed",
|
||||
duration_ms: Date.now() - startedAt,
|
||||
error_type: err instanceof Error ? err.name : "UnknownError",
|
||||
});
|
||||
return {
|
||||
structuredContent: structuredToolResult(
|
||||
name,
|
||||
false,
|
||||
undefined,
|
||||
err instanceof Error ? err.message : String(err),
|
||||
),
|
||||
content: [
|
||||
{
|
||||
type: "text" as const,
|
||||
text: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (authorityWithheld > 0) {
|
||||
debugLog(
|
||||
`Withheld ${authorityWithheld} tool(s) from tools/list — not permitted by the ` +
|
||||
`active capability profile (${[...capabilities.capabilities].sort().join(", ")}). ` +
|
||||
`Call get_capabilities to see the enabled authority.`,
|
||||
);
|
||||
}
|
||||
if (packWithheld > 0) {
|
||||
debugLog(
|
||||
`Withheld ${packWithheld} permitted tool(s) from tools/list by the ` +
|
||||
`${toolPacks.fullCatalog ? "full" : toolPacks.selected.join(", ")} workflow tool pack selection.`,
|
||||
);
|
||||
}
|
||||
|
||||
// Register LLM instructions resource
|
||||
server.registerResource(
|
||||
"premiere-instructions",
|
||||
"config://premiere-instructions",
|
||||
{
|
||||
description:
|
||||
"Instructions and best practices for using Premiere Pro via MCP tools",
|
||||
mimeType: "text/plain",
|
||||
},
|
||||
async (uri) => ({
|
||||
contents: [
|
||||
{
|
||||
uri: uri.href,
|
||||
text: premiereInstructions,
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
for (const resource of getLiveContextResources(bridgeOptions)) {
|
||||
server.registerResource(
|
||||
resource.name,
|
||||
resource.uri,
|
||||
{
|
||||
description: resource.description,
|
||||
mimeType: "application/json",
|
||||
},
|
||||
resource.read,
|
||||
);
|
||||
}
|
||||
|
||||
server.registerResource(
|
||||
"premiere-workflows",
|
||||
"config://premiere-workflows",
|
||||
{
|
||||
description: "High-level, safety-oriented Premiere Pro workflow catalog",
|
||||
mimeType: "application/json",
|
||||
},
|
||||
async (uri) => ({
|
||||
contents: [
|
||||
{
|
||||
uri: uri.href,
|
||||
mimeType: "application/json",
|
||||
text: WORKFLOW_RESOURCE,
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
server.registerResource(
|
||||
"premiere-project-context",
|
||||
"config://premiere-project-context",
|
||||
{
|
||||
description: "Revisioned local project-context indexing and retrieval workflow",
|
||||
mimeType: "application/json",
|
||||
},
|
||||
async (uri) => ({
|
||||
contents: [
|
||||
{
|
||||
uri: uri.href,
|
||||
mimeType: "application/json",
|
||||
text: PROJECT_CONTEXT_RESOURCE,
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
for (const prompt of WORKFLOW_PROMPTS) {
|
||||
server.registerPrompt(
|
||||
prompt.name,
|
||||
{
|
||||
title: prompt.title,
|
||||
description: prompt.description,
|
||||
argsSchema: z.object(prompt.argsSchema),
|
||||
},
|
||||
prompt.render,
|
||||
);
|
||||
}
|
||||
|
||||
// Register ExtendScript API reference resource
|
||||
server.registerResource(
|
||||
"extendscript-reference",
|
||||
"config://extendscript-reference",
|
||||
{
|
||||
description:
|
||||
"Complete Premiere Pro ExtendScript API reference for writing custom scripts via execute_extendscript",
|
||||
mimeType: "text/plain",
|
||||
},
|
||||
async (uri) => ({
|
||||
contents: [
|
||||
{
|
||||
uri: uri.href,
|
||||
text: EXTENDSCRIPT_REFERENCE,
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
const toolCount = Object.keys(toolModules).length;
|
||||
debugLog(
|
||||
`Registered ${toolCount} tools + 14 resources + ${WORKFLOW_PROMPTS.length} prompts`,
|
||||
);
|
||||
|
||||
return server;
|
||||
}
|
||||
Reference in New Issue
Block a user