@toolplex/client
Version:
The official ToolPlex client for AI agent tool discovery and execution
82 lines (81 loc) • 3.84 kB
JavaScript
import Registry from "../registry.js";
export class PlaybookPolicy {
constructor(clientContext, callToolObserver) {
this.callToolObserver = callToolObserver;
this.clientContext = clientContext;
this.blockedMcpServersSet = new Set(clientContext.flags.blocked_mcp_servers || []);
}
/**
* Validates a playbook before saving by checking that:
* - Referenced servers and tools have been used
* - No blocked servers are referenced
* - Private playbooks are only created when enabled
*
* For each action with a 'call' property, verifies that:
* - Any referenced server has been connected to
* - Any referenced server/tool combination has been executed
* - The server is not in the blocked servers list
*
* @throws Error if a referenced server or tool has not been used in the current session,
* if a blocked server is referenced, or if trying to create a private playbook when disabled
*/
enforceSavePlaybookPolicy(playbook) {
if (!Array.isArray(playbook.actions)) {
throw new Error("Playbook actions must be an array");
}
for (const [idx, action] of playbook.actions.entries()) {
if (!action.call)
continue;
// Parse the call string
// Supported formats:
// - "mcp_server_id:<server_id>::<tool_name>"
// - "mcp_server_id:<server_id>"
// - "playbook_id:<playbook_id>"
const call = action.call.trim();
if (call.startsWith("mcp_server_id:")) {
// Could be with or without tool_name
// e.g. mcp_server_id:abc123::toolX or mcp_server_id:abc123
const match = call.match(/^mcp_server_id:([^:]+)(?:::([^:]+))?$/);
if (!match) {
throw new Error(`Invalid call format in action ${idx + 1}: "${call}"`);
}
const serverId = match[1];
const toolName = match[2];
// Check if server is blocked
if (this.blockedMcpServersSet.has(serverId)) {
throw new Error(`Playbook action ${idx + 1} references blocked server "${serverId}"`);
}
if (toolName) {
// Must have called this tool on this server
if (!this.callToolObserver.wasToolCalled(serverId, toolName)) {
throw new Error(`Playbook action ${idx + 1} references tool "${toolName}" on server "${serverId}" which has not been used in this session.`);
}
}
else {
// Only server referenced, must have called any tool on this server
if (!this.callToolObserver.wasServerCalled(serverId)) {
throw new Error(`Playbook action ${idx + 1} references server "${serverId}" which has not been used in this session.`);
}
}
}
else if (call.startsWith("playbook_id:")) {
// For playbook references, we could skip or add logic if needed
// For now, we do not validate playbook_id usage
continue;
}
else {
throw new Error(`Playbook action ${idx + 1} has an unrecognized call format: "${call}"`);
}
}
}
/**
* Validates if playbook usage logging is allowed based on read-only mode
* @throws Error if read-only mode is enabled
*/
enforceLogPlaybookUsagePolicy() {
if (this.clientContext.permissions.enable_read_only_mode) {
const promptsCache = Registry.getPromptsCache();
throw new Error(promptsCache.getPrompt("log_playbook_usage_disabled"));
}
}
}