@synthetic-ai/premiere-mcp 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +135 -73
  2. package/LICENSE +21 -21
  3. package/README.md +242 -242
  4. package/cep-plugin/.debug +8 -8
  5. package/cep-plugin/CSInterface.js +92 -70
  6. package/cep-plugin/CSXS/manifest.xml +49 -49
  7. package/cep-plugin/host.jsx +7 -7
  8. package/cep-plugin/index.html +472 -419
  9. package/cep-plugin/main.js +683 -327
  10. package/cep-plugin/premiere.jsx +24947 -24947
  11. package/dist/bridge/script-builder.js +378 -378
  12. package/dist/index.js +117 -18
  13. package/dist/license.d.ts +22 -0
  14. package/dist/license.js +89 -0
  15. package/dist/server.js +67 -61
  16. package/dist/tools/advanced.js +357 -357
  17. package/dist/tools/audio-advanced.js +1049 -1049
  18. package/dist/tools/audio.js +42 -42
  19. package/dist/tools/captions.js +27 -27
  20. package/dist/tools/clipboard.js +214 -214
  21. package/dist/tools/color-advanced.js +253 -253
  22. package/dist/tools/discovery.js +291 -291
  23. package/dist/tools/effects.js +285 -285
  24. package/dist/tools/export-presets.js +206 -206
  25. package/dist/tools/export.js +255 -255
  26. package/dist/tools/health.js +10 -10
  27. package/dist/tools/inspection.js +885 -885
  28. package/dist/tools/keyframes.js +369 -369
  29. package/dist/tools/markers.js +89 -89
  30. package/dist/tools/media.js +178 -178
  31. package/dist/tools/metadata.js +94 -94
  32. package/dist/tools/multicam.js +50 -50
  33. package/dist/tools/playback.js +15 -15
  34. package/dist/tools/playhead.js +95 -95
  35. package/dist/tools/project-manager.js +21 -21
  36. package/dist/tools/project.js +149 -149
  37. package/dist/tools/scripting.js +319 -319
  38. package/dist/tools/selection.js +202 -202
  39. package/dist/tools/sequence-advanced.js +379 -374
  40. package/dist/tools/sequence-settings.js +564 -556
  41. package/dist/tools/sequence.js +169 -150
  42. package/dist/tools/source-monitor.js +68 -68
  43. package/dist/tools/text.js +58 -58
  44. package/dist/tools/timeline.js +312 -312
  45. package/dist/tools/track-management.js +446 -446
  46. package/dist/tools/track-targeting.js +635 -635
  47. package/dist/tools/tracks.js +87 -87
  48. package/dist/tools/transitions-motion.js +174 -174
  49. package/dist/tools/transitions.js +111 -111
  50. package/dist/tools/utility.js +606 -606
  51. package/dist/tools/workspace.js +29 -29
  52. package/package.json +1 -1
  53. package/scripts/build-chat-dmg.sh +210 -210
  54. package/scripts/build-chat-zip.sh +291 -291
  55. package/scripts/install-cep-windows.ps1 +99 -99
  56. package/scripts/install-cep.sh +66 -66
  57. package/scripts/install-chat-plugin.bat +67 -67
  58. package/scripts/install-chat-plugin.sh +85 -85
  59. package/scripts/install-windows.bat +95 -95
  60. package/scripts/package.mjs +254 -218
package/dist/index.js CHANGED
@@ -2,29 +2,120 @@
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  import { createServer } from "./server.js";
4
4
  import { cleanupTempDir, getTempDir } from "./bridge/file-bridge.js";
5
+ import { checkLicense } from "./license.js";
5
6
  import { execSync } from "child_process";
6
7
  import { fileURLToPath } from "url";
7
8
  import path from "path";
9
+ import { existsSync, mkdirSync, writeFileSync, unlinkSync } from "fs";
8
10
  const __filename = fileURLToPath(import.meta.url);
9
11
  const __dirname = path.dirname(__filename);
10
12
  const projectRoot = path.resolve(__dirname, "..");
13
+ // ---------------------------------------------------------------------------
14
+ // --doctor — standalone diagnostics, no MCP client or Premiere Pro required
15
+ // to run it. Prints a PASS/WARN/FAIL summary and exits 1 if anything failed,
16
+ // so it's also usable in a script (`premiere-mcp --doctor || echo "broken"`).
17
+ // Defined before the flag-handling block below since it's called from there.
18
+ // ---------------------------------------------------------------------------
19
+ const GREEN = "\x1b[32m";
20
+ const RED = "\x1b[31m";
21
+ const YELLOW = "\x1b[33m";
22
+ const DIM = "\x1b[2m";
23
+ const RESET = "\x1b[0m";
24
+ function printCheck(result, label, detail) {
25
+ const icon = result === "pass" ? `${GREEN}✓${RESET}` : result === "warn" ? `${YELLOW}!${RESET}` : `${RED}✗${RESET}`;
26
+ console.log(` ${icon} ${label}${detail ? `\n ${DIM}${detail}${RESET}` : ""}`);
27
+ }
28
+ function premiereInstallCandidates() {
29
+ const years = ["2026", "2025", "2024", "2023", "2022"];
30
+ if (process.platform === "darwin") {
31
+ return years.map((y) => `/Applications/Adobe Premiere Pro ${y}/Adobe Premiere Pro ${y}.app`);
32
+ }
33
+ return years.map((y) => `C:\\Program Files\\Adobe\\Adobe Premiere Pro ${y}\\Adobe Premiere Pro.exe`);
34
+ }
35
+ // Returns whether any check failed — the caller sets process.exitCode rather
36
+ // than us calling process.exit() here. On Windows, forcing process.exit()
37
+ // immediately after an in-flight fetch/AbortController teardown (the live
38
+ // license check below) can crash with a libuv assertion
39
+ // ("UV_HANDLE_CLOSING", src/win/async.c) before the process actually exits.
40
+ // Setting exitCode and letting the event loop drain naturally avoids it.
41
+ async function runDoctor() {
42
+ console.log(`\n${DIM}premiere-mcp doctor — checking your setup${RESET}\n`);
43
+ let hasFail = false;
44
+ // 1. Node version
45
+ const nodeMajor = parseInt(process.version.slice(1).split(".")[0], 10);
46
+ if (nodeMajor >= 18) {
47
+ printCheck("pass", `Node.js ${process.version}`);
48
+ }
49
+ else {
50
+ hasFail = true;
51
+ printCheck("fail", `Node.js ${process.version}`, "Requires Node 18 or newer — update from nodejs.org");
52
+ }
53
+ // 2. OS (informational — both are supported, this just confirms detection)
54
+ const osLabel = process.platform === "darwin" ? "macOS" : process.platform === "win32" ? "Windows" : process.platform;
55
+ printCheck(process.platform === "darwin" || process.platform === "win32" ? "pass" : "warn", `OS: ${osLabel}`);
56
+ // 3. Premiere Pro install detected on disk
57
+ const found = premiereInstallCandidates().find((p) => existsSync(p));
58
+ if (found) {
59
+ printCheck("pass", "Premiere Pro detected", found);
60
+ }
61
+ else {
62
+ printCheck("warn", "Premiere Pro not found in the usual install location", "Not fatal — the MCP server only needs Premiere Pro running when a tool actually calls it.");
63
+ }
64
+ // 4. License key present
65
+ const licenseKey = process.env.PREMIERE_PRO_MCP_LICENSE;
66
+ if (!licenseKey) {
67
+ hasFail = true;
68
+ printCheck("fail", "PREMIERE_PRO_MCP_LICENSE not set", "Add it to your MCP client config (claude_desktop_config.json or equivalent).");
69
+ }
70
+ else {
71
+ printCheck("pass", `License key present (${licenseKey.slice(0, 8)}...)`);
72
+ // 5. Live validation against synthetic.com.ar (also exercises network reachability)
73
+ const result = await checkLicense();
74
+ if (result.valid) {
75
+ printCheck("pass", "License validates", result.expiresAt ? `Expires: ${result.expiresAt}` : "No expiration");
76
+ }
77
+ else {
78
+ hasFail = true;
79
+ printCheck("fail", "License validation failed", result.message ?? "Unknown reason");
80
+ }
81
+ }
82
+ // 6. Bridge temp directory writable
83
+ const tempDir = getTempDir();
84
+ try {
85
+ if (!existsSync(tempDir))
86
+ mkdirSync(tempDir, { recursive: true, mode: 0o700 });
87
+ const probe = path.join(tempDir, `.doctor-probe-${Date.now()}`);
88
+ writeFileSync(probe, "ok");
89
+ unlinkSync(probe);
90
+ printCheck("pass", "Bridge temp directory writable", tempDir);
91
+ }
92
+ catch (err) {
93
+ hasFail = true;
94
+ printCheck("fail", "Bridge temp directory not writable", `${tempDir} — ${err instanceof Error ? err.message : String(err)}`);
95
+ }
96
+ console.log(hasFail
97
+ ? `\n${RED}Some checks failed.${RESET} Fix the items above, or share this output with support at https://synthetic.com.ar/dashboard/support\n`
98
+ : `\n${GREEN}Everything looks good.${RESET}\n`);
99
+ return hasFail;
100
+ }
11
101
  // Handle CLI flags
12
102
  const args = process.argv.slice(2);
13
103
  if (args.includes("--help") || args.includes("-h")) {
14
- console.log(`
15
- premiere-pro-mcp — MCP server for Adobe Premiere Pro (437 tools)
16
-
17
- Usage:
18
- premiere-pro-mcp Start the MCP server (stdio transport)
19
- premiere-pro-mcp --install-cep Install the CEP plugin into Premiere Pro
20
- premiere-pro-mcp --help Show this help message
21
- premiere-pro-mcp --version Show version
22
-
23
- Environment variables:
24
- PREMIERE_TEMP_DIR Shared temp directory (default: OS temp + /premiere-mcp-bridge)
25
- PREMIERE_TIMEOUT_MS Command timeout in ms (default: 30000)
26
-
27
- More info: https://synthetic.com.ar
104
+ console.log(`
105
+ premiere-pro-mcp — MCP server for Adobe Premiere Pro (437 tools)
106
+
107
+ Usage:
108
+ premiere-pro-mcp Start the MCP server (stdio transport)
109
+ premiere-pro-mcp --doctor Check your setup (Node, license, Premiere Pro, temp dir)
110
+ premiere-pro-mcp --install-cep Install the CEP plugin into Premiere Pro
111
+ premiere-pro-mcp --help Show this help message
112
+ premiere-pro-mcp --version Show version
113
+
114
+ Environment variables:
115
+ PREMIERE_TEMP_DIR Shared temp directory (default: OS temp + /premiere-mcp-bridge)
116
+ PREMIERE_TIMEOUT_MS Command timeout in ms (default: 30000)
117
+
118
+ More info: https://synthetic.com.ar
28
119
  `);
29
120
  process.exit(0);
30
121
  }
@@ -33,6 +124,12 @@ if (args.includes("--version") || args.includes("-v")) {
33
124
  console.log(pkg.default.version);
34
125
  process.exit(0);
35
126
  }
127
+ let ranAsFlag = false;
128
+ if (args.includes("--doctor")) {
129
+ const hasFail = await runDoctor();
130
+ process.exitCode = hasFail ? 1 : 0;
131
+ ranAsFlag = true;
132
+ }
36
133
  if (args.includes("--install-cep")) {
37
134
  const scriptPath = path.join(projectRoot, "scripts", "install-cep.sh");
38
135
  console.log("Installing CEP plugin...\n");
@@ -63,8 +160,10 @@ async function main() {
63
160
  await server.connect(transport);
64
161
  console.error(`[premiere-pro-mcp] Server connected and ready`);
65
162
  }
66
- main().catch((err) => {
67
- console.error("[premiere-pro-mcp] Fatal error:", err);
68
- process.exit(1);
69
- });
163
+ if (!ranAsFlag) {
164
+ main().catch((err) => {
165
+ console.error("[premiere-pro-mcp] Fatal error:", err);
166
+ process.exit(1);
167
+ });
168
+ }
70
169
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * License validation — gates tool calls on a valid PREMIERE_PRO_MCP_LICENSE.
3
+ * Same pattern as ae-mcp/src/server.mjs and photoshop-mcp/server.mjs:
4
+ * validate once, cache for an hour, let diagnostic tools through for free.
5
+ */
6
+ type LicenseResult = {
7
+ valid: boolean;
8
+ plan?: string | null;
9
+ expiresAt?: string | null;
10
+ message?: string;
11
+ };
12
+ export declare function checkLicense(): Promise<LicenseResult>;
13
+ export declare function licenseErrorResponse(toolName: string): {
14
+ content: {
15
+ type: "text";
16
+ text: string;
17
+ }[];
18
+ isError: boolean;
19
+ };
20
+ export declare const FREE_TOOLS: Set<string>;
21
+ export {};
22
+ //# sourceMappingURL=license.d.ts.map
@@ -0,0 +1,89 @@
1
+ /**
2
+ * License validation — gates tool calls on a valid PREMIERE_PRO_MCP_LICENSE.
3
+ * Same pattern as ae-mcp/src/server.mjs and photoshop-mcp/server.mjs:
4
+ * validate once, cache for an hour, let diagnostic tools through for free.
5
+ */
6
+ const LICENSE_KEY = process.env.PREMIERE_PRO_MCP_LICENSE;
7
+ const LICENSE_VALIDATION_URL = "https://synthetic.com.ar/api/v1/license/validate";
8
+ const LICENSE_CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
9
+ let cache = null;
10
+ async function postJson(url, body) {
11
+ try {
12
+ const controller = new AbortController();
13
+ const timeout = setTimeout(() => controller.abort(), 5000);
14
+ const res = await fetch(url, {
15
+ method: "POST",
16
+ headers: { "Content-Type": "application/json" },
17
+ body: JSON.stringify(body),
18
+ signal: controller.signal,
19
+ });
20
+ clearTimeout(timeout);
21
+ return (await res.json());
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ export async function checkLicense() {
28
+ if (cache && Date.now() - cache.checkedAt < LICENSE_CACHE_TTL_MS) {
29
+ return cache;
30
+ }
31
+ if (!LICENSE_KEY) {
32
+ cache = {
33
+ valid: false,
34
+ plan: null,
35
+ message: "No license key. Set PREMIERE_PRO_MCP_LICENSE in your MCP config. Get a license at https://synthetic.com.ar/premiere-mcp",
36
+ checkedAt: Date.now(),
37
+ };
38
+ return cache;
39
+ }
40
+ try {
41
+ const result = await postJson(LICENSE_VALIDATION_URL, {
42
+ key: LICENSE_KEY,
43
+ product: "premiere-mcp",
44
+ });
45
+ if (result) {
46
+ cache = { ...result, checkedAt: Date.now() };
47
+ }
48
+ else if (cache && cache.valid) {
49
+ // Transient network failure — extend the grace period on an
50
+ // already-valid cached result instead of locking the user out.
51
+ cache = { ...cache, checkedAt: Date.now() };
52
+ }
53
+ else {
54
+ cache = {
55
+ valid: false,
56
+ message: "License server unreachable. Check your internet connection.",
57
+ checkedAt: Date.now(),
58
+ };
59
+ }
60
+ }
61
+ catch {
62
+ cache = {
63
+ valid: false,
64
+ message: "License server unreachable. Check your internet connection.",
65
+ checkedAt: Date.now(),
66
+ };
67
+ }
68
+ return cache;
69
+ }
70
+ export function licenseErrorResponse(toolName) {
71
+ const msg = cache?.message ?? "Invalid or missing license.";
72
+ return {
73
+ content: [
74
+ {
75
+ type: "text",
76
+ text: JSON.stringify({
77
+ error: "License required",
78
+ detail: msg,
79
+ tool: toolName,
80
+ info: "Get a license at https://synthetic.com.ar/premiere-mcp",
81
+ }),
82
+ },
83
+ ],
84
+ isError: true,
85
+ };
86
+ }
87
+ // Tools that work without a license (diagnostics / free tier).
88
+ export const FREE_TOOLS = new Set(["ping", "get_version_info"]);
89
+ //# sourceMappingURL=license.js.map
package/dist/server.js CHANGED
@@ -36,67 +36,68 @@ import { getMulticamTools } from "./tools/multicam.js";
36
36
  import { getExportPresetsTools } from "./tools/export-presets.js";
37
37
  import { getSequenceSettingsTools } from "./tools/sequence-settings.js";
38
38
  import { EXTENDSCRIPT_REFERENCE } from "./resources/extendscript-reference.js";
39
+ import { checkLicense, licenseErrorResponse, FREE_TOOLS } from "./license.js";
39
40
  import { z } from "zod";
40
- const PREMIERE_INSTRUCTIONS = `You are controlling Adobe Premiere Pro through MCP tools. Follow these best practices:
41
-
42
- WORKFLOW ORDER:
43
- 1. Always call get_project_info first to understand the current state.
44
- 2. Import media before adding to timeline.
45
- 3. Create/select a sequence before timeline operations.
46
- 4. Add clips first, then effects, then transitions.
47
- 5. Save the project after making significant changes.
48
-
49
- TIMELINE RULES:
50
- - Clips are identified by node_id. Use get_active_sequence or list_sequence_tracks to discover node IDs.
51
- - Video clips on higher track indices appear on top of lower ones (compositing order).
52
- - Images default to ~5 seconds duration when added to timeline.
53
- - The first clip added to a new sequence determines its resolution and frame rate.
54
- - Time values are in seconds (the tools handle tick conversion internally).
55
-
56
- EFFECTS & TRANSITIONS:
57
- - Apply effects by name using apply_effect (e.g., "Gaussian Blur", "Lumetri Color").
58
- - Use list_available_effects to find exact effect names.
59
- - Transitions require clips to be adjacent (no gap between them).
60
- - Keep transitions short (0.5-2 seconds typically).
61
- - Use color_correct for Lumetri Color adjustments rather than manual property setting.
62
-
63
- KEYFRAMES:
64
- - Use get_effect_properties to discover property names before setting values.
65
- - Enable keyframes with add_keyframe; the property auto-enables time-varying.
66
- - Interpolation types: "linear" (smooth), "hold" (instant jump), "bezier" (custom easing).
67
-
68
- QE DOM TOOLS:
69
- - Tools marked "Uses QE DOM" use an undocumented API. They are powerful but may behave unexpectedly.
70
- - ripple_delete, roll_edit, slide_edit, slip_edit are QE-based advanced trim tools.
71
- - set_clip_speed_qe is more reliable than the ExtendScript speed method.
72
-
73
- CLIPS & SELECTION:
74
- - Use set_clip_selection to select clips before operations that work on selection (link, unlink, scene_edit_detection).
75
- - Use overwrite_clip for 3-point editing (overwrites existing content).
76
- - Use add_to_timeline for insert editing (ripples content forward).
77
-
78
- BINS & ORGANIZATION:
79
- - Bins are folders in the project panel. Use create_bin, delete_bin, rename_bin.
80
- - Use move_item_to_bin to organize imported media.
81
- - create_smart_bin creates auto-populating search bins.
82
-
83
- EXPORT:
84
- - Use export_sequence for AME-based encoding with presets.
85
- - Use export_frame to capture a single frame as an image.
86
- - Use start_batch_encode to begin rendering all queued items.
87
-
88
- ERROR HANDLING:
89
- - If a tool returns "No active sequence", call set_active_sequence first.
90
- - If a tool returns "Clip not found", the node_id may have changed after timeline edits. Re-query the sequence.
91
- - If "QE clip not found", the clip index may differ between DOM and QE. Try re-querying.
92
-
93
- CUSTOM SCRIPTING:
94
- - Use execute_extendscript to write and run any ExtendScript code for tasks not covered by existing tools.
95
- - Use evaluate_expression for quick one-line queries.
96
- - Use inspect_dom_object to explore unfamiliar objects.
97
- - Use get_premiere_state as your first call to understand the full current context.
98
- - Use get_sequence_structure for detailed timeline layout before edits.
99
- - Read the "extendscript-reference" resource for the complete API cheat sheet.
41
+ const PREMIERE_INSTRUCTIONS = `You are controlling Adobe Premiere Pro through MCP tools. Follow these best practices:
42
+
43
+ WORKFLOW ORDER:
44
+ 1. Always call get_project_info first to understand the current state.
45
+ 2. Import media before adding to timeline.
46
+ 3. Create/select a sequence before timeline operations.
47
+ 4. Add clips first, then effects, then transitions.
48
+ 5. Save the project after making significant changes.
49
+
50
+ TIMELINE RULES:
51
+ - Clips are identified by node_id. Use get_active_sequence or list_sequence_tracks to discover node IDs.
52
+ - Video clips on higher track indices appear on top of lower ones (compositing order).
53
+ - Images default to ~5 seconds duration when added to timeline.
54
+ - The first clip added to a new sequence determines its resolution and frame rate.
55
+ - Time values are in seconds (the tools handle tick conversion internally).
56
+
57
+ EFFECTS & TRANSITIONS:
58
+ - Apply effects by name using apply_effect (e.g., "Gaussian Blur", "Lumetri Color").
59
+ - Use list_available_effects to find exact effect names.
60
+ - Transitions require clips to be adjacent (no gap between them).
61
+ - Keep transitions short (0.5-2 seconds typically).
62
+ - Use color_correct for Lumetri Color adjustments rather than manual property setting.
63
+
64
+ KEYFRAMES:
65
+ - Use get_effect_properties to discover property names before setting values.
66
+ - Enable keyframes with add_keyframe; the property auto-enables time-varying.
67
+ - Interpolation types: "linear" (smooth), "hold" (instant jump), "bezier" (custom easing).
68
+
69
+ QE DOM TOOLS:
70
+ - Tools marked "Uses QE DOM" use an undocumented API. They are powerful but may behave unexpectedly.
71
+ - ripple_delete, roll_edit, slide_edit, slip_edit are QE-based advanced trim tools.
72
+ - set_clip_speed_qe is more reliable than the ExtendScript speed method.
73
+
74
+ CLIPS & SELECTION:
75
+ - Use set_clip_selection to select clips before operations that work on selection (link, unlink, scene_edit_detection).
76
+ - Use overwrite_clip for 3-point editing (overwrites existing content).
77
+ - Use add_to_timeline for insert editing (ripples content forward).
78
+
79
+ BINS & ORGANIZATION:
80
+ - Bins are folders in the project panel. Use create_bin, delete_bin, rename_bin.
81
+ - Use move_item_to_bin to organize imported media.
82
+ - create_smart_bin creates auto-populating search bins.
83
+
84
+ EXPORT:
85
+ - Use export_sequence for AME-based encoding with presets.
86
+ - Use export_frame to capture a single frame as an image.
87
+ - Use start_batch_encode to begin rendering all queued items.
88
+
89
+ ERROR HANDLING:
90
+ - If a tool returns "No active sequence", call set_active_sequence first.
91
+ - If a tool returns "Clip not found", the node_id may have changed after timeline edits. Re-query the sequence.
92
+ - If "QE clip not found", the clip index may differ between DOM and QE. Try re-querying.
93
+
94
+ CUSTOM SCRIPTING:
95
+ - Use execute_extendscript to write and run any ExtendScript code for tasks not covered by existing tools.
96
+ - Use evaluate_expression for quick one-line queries.
97
+ - Use inspect_dom_object to explore unfamiliar objects.
98
+ - Use get_premiere_state as your first call to understand the full current context.
99
+ - Use get_sequence_structure for detailed timeline layout before edits.
100
+ - Read the "extendscript-reference" resource for the complete API cheat sheet.
100
101
  `;
101
102
  /**
102
103
  * Convert a JSON Schema-style parameters object to a Zod shape for MCP SDK registration.
@@ -146,7 +147,7 @@ function jsonSchemaToZodShape(params) {
146
147
  export function createServer(bridgeOptions) {
147
148
  const server = new McpServer({
148
149
  name: "premiere-pro-mcp",
149
- version: "1.1.1",
150
+ version: "1.1.7",
150
151
  });
151
152
  // Collect all tools from each module
152
153
  const toolModules = {
@@ -191,6 +192,11 @@ export function createServer(bridgeOptions) {
191
192
  for (const [name, tool] of Object.entries(toolModules)) {
192
193
  const zodShape = jsonSchemaToZodShape(tool.parameters);
193
194
  server.tool(name, tool.description, zodShape, async (args) => {
195
+ if (!FREE_TOOLS.has(name)) {
196
+ const lic = await checkLicense();
197
+ if (!lic.valid)
198
+ return licenseErrorResponse(name);
199
+ }
194
200
  try {
195
201
  const result = await tool.handler(args);
196
202
  if (result.success) {