Skip to main content
Glama
rafteles2016

MCP Dynamics CRM Server

by rafteles2016

dynamics_list_plugin_assemblies

Lists registered plugin assemblies in Dynamics CRM to manage custom business logic and integrations. Filter by assembly name or entity to organize development workflows.

Instructions

Lista os assemblies de plugins registrados no Dynamics CRM

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
assemblyNameNoFiltrar por nome do assembly
entityLogicalNameNoFiltrar por entidade

Implementation Reference

  • The handler implementation for the dynamics_list_plugin_assemblies tool.
    server.tool(
      "dynamics_list_plugin_assemblies",
      "Lista os assemblies de plugins registrados no Dynamics CRM",
      ListPluginsSchema.shape,
      async (params: z.infer<typeof ListPluginsSchema>) => {
        let filter = "ishidden/Value eq false";
        if (params.assemblyName) {
          filter += ` and contains(name,'${params.assemblyName}')`;
        }
    
        const result = await client.list("pluginassemblies", {
          select: ["pluginassemblyid", "name", "version", "publickeytoken", "culture", "description", "createdon", "modifiedon"],
          filter,
          orderby: "name asc",
        });
    
        return {
          content: [
            {
              type: "text" as const,
              text: `Encontrados ${result.value.length} assemblies:\n\n${JSON.stringify(result.value, null, 2)}`,
            },
          ],
        };
      }
    );
  • The input schema definition for the tool.
    export const ListPluginsSchema = z.object({
      assemblyName: z.string().optional().describe("Filtrar por nome do assembly"),
      entityLogicalName: z.string().optional().describe("Filtrar por entidade"),
    });
  • Registration of the plugin tools, including dynamics_list_plugin_assemblies.
    export function registerPluginTools(
      server: { tool: Function },
      client: DataverseClient
    ) {
      // 1. Generate Plugin Code
      server.tool(
        "dynamics_generate_plugin_code",
        "Gera código C# para um plugin do Dynamics CRM com base nos parâmetros fornecidos",
        CreatePluginSchema.shape,
        async (params: z.infer<typeof CreatePluginSchema>) => {
          const stageValue = STAGE_MAP[params.stage] || 40;
          const template = PLUGIN_TEMPLATES.standard;
    
          const code = template
            .replace(/{{CLASS_NAME}}/g, params.name)
            .replace(/{{ENTITY_LOGICAL_NAME}}/g, params.entityLogicalName)
            .replace(/{{MESSAGE}}/g, params.message)
            .replace(/{{STAGE}}/g, String(stageValue))
            .replace(/{{EXECUTION_MODE}}/g, params.executionMode === "Synchronous" ? "0" : "1")
            .replace(/{{FILTERING_ATTRIBUTES}}/g, params.filteringAttributes || "")
            .replace(/{{DESCRIPTION}}/g, params.description || `Plugin ${params.name} for ${params.message} on ${params.entityLogicalName}`)
            .replace(/{{BUSINESS_LOGIC}}/g, params.businessLogic || "// TODO: Implement business logic here");
    
          return {
            content: [
              {
                type: "text" as const,
                text: `Plugin C# gerado com sucesso:\n\n\`\`\`csharp\n${code}\n\`\`\`\n\n**Configuração:**\n- Entidade: ${params.entityLogicalName}\n- Mensagem: ${params.message}\n- Estágio: ${params.stage} (${stageValue})\n- Modo: ${params.executionMode}\n${params.filteringAttributes ? `- Filtro: ${params.filteringAttributes}` : ""}`,
              },
            ],
          };
        }
      );
    
      // 2. List Plugin Assemblies
      server.tool(
        "dynamics_list_plugin_assemblies",
        "Lista os assemblies de plugins registrados no Dynamics CRM",
        ListPluginsSchema.shape,
        async (params: z.infer<typeof ListPluginsSchema>) => {
          let filter = "ishidden/Value eq false";
          if (params.assemblyName) {
            filter += ` and contains(name,'${params.assemblyName}')`;
          }
    
          const result = await client.list("pluginassemblies", {
            select: ["pluginassemblyid", "name", "version", "publickeytoken", "culture", "description", "createdon", "modifiedon"],
            filter,
            orderby: "name asc",
          });
    
          return {
            content: [
              {
                type: "text" as const,
                text: `Encontrados ${result.value.length} assemblies:\n\n${JSON.stringify(result.value, null, 2)}`,
              },
            ],
          };
        }
      );
    
      // 3. List Plugin Types
      server.tool(
        "dynamics_list_plugin_types",
        "Lista os tipos de plugins (classes) de um assembly",
        z.object({ assemblyId: z.string().describe("ID do assembly") }).shape,
        async (params: { assemblyId: string }) => {
          const result = await client.list("plugintypes", {
            select: ["plugintypeid", "name", "friendlyname", "typename", "assemblyname", "description"],
            filter: `pluginassemblyid eq '${params.assemblyId}'`,
            orderby: "name asc",
          });
    
          return {
            content: [
              {
                type: "text" as const,
                text: `Tipos de plugin encontrados: ${result.value.length}\n\n${JSON.stringify(result.value, null, 2)}`,
              },
            ],
          };
        }
      );
    
      // 4. List Plugin Steps
      server.tool(
        "dynamics_list_plugin_steps",
        "Lista os steps registrados para plugins, opcionalmente filtrados por entidade",
        z.object({
          entityLogicalName: z.string().optional().describe("Filtrar por entidade"),
          pluginTypeId: z.string().optional().describe("Filtrar por tipo de plugin"),
        }).shape,
        async (params: { entityLogicalName?: string; pluginTypeId?: string }) => {
          const filters: string[] = [];
          if (params.entityLogicalName) {
            filters.push(`configuration eq '${params.entityLogicalName}' or sdkmessagefilterid/primaryobjecttypecode eq '${params.entityLogicalName}'`);
          }
          if (params.pluginTypeId) {
            filters.push(`_plugintypeid_value eq '${params.pluginTypeId}'`);
          }
    
          const result = await client.list("sdkmessageprocessingsteps", {
            select: [
              "sdkmessageprocessingstepid", "name", "stage", "mode",
              "rank", "statecode", "filteringattributes", "description",
              "_sdkmessageid_value", "_plugintypeid_value",
            ],
            filter: filters.length > 0 ? filters.join(" and ") : undefined,
            orderby: "name asc",
          });
    
          return {
            content: [
              {
                type: "text" as const,
                text: `Steps encontrados: ${result.value.length}\n\n${JSON.stringify(result.value, null, 2)}`,
              },
            ],
          };
        }
      );
    
      // 5. Register Plugin Step
      server.tool(
        "dynamics_register_plugin_step",
        "Registra um novo step de plugin no Dynamics CRM",
        RegisterPluginStepSchema.shape,
        async (params: z.infer<typeof RegisterPluginStepSchema>) => {
          const stepData = {
            name: params.name,
            stage: params.stage,
            mode: params.executionMode,
            rank: params.rank,
            "plugintypeid@odata.bind": `/plugintypes(${params.pluginTypeId})`,
            "sdkmessageid@odata.bind": `/sdkmessages?$filter=name eq '${params.message}'`,
            filteringattributes: params.filteringAttributes || null,
            supporteddeployment: 0, // Server only
          };
    
          // Get the message filter for the entity
          const messageFilter = await client.list("sdkmessagefilters", {
            select: ["sdkmessagefilterid"],
            filter: `primaryobjecttypecode eq '${params.entityLogicalName}' and sdkmessageid/name eq '${params.message}'`,
            top: 1,
          });
    
          if (messageFilter.value.length > 0) {
            const filterId = (messageFilter.value[0] as Record<string, unknown>).sdkmessagefilterid;
            (stepData as Record<string, unknown>)["sdkmessagefilterid@odata.bind"] = `/sdkmessagefilters(${filterId})`;
          }
    
          const result = await client.create("sdkmessageprocessingsteps", stepData);
    
          return {
            content: [
              {
                type: "text" as const,
                text: `Step registrado com sucesso!\nID: ${result.entityId}\nNome: ${params.name}\nMensagem: ${params.message}\nEntidade: ${params.entityLogicalName}\nEstágio: ${params.stage}`,
              },
            ],
          };
        }
      );
    
      // 6. Enable/Disable Plugin Step
      server.tool(
        "dynamics_toggle_plugin_step",
        "Ativa ou desativa um step de plugin",
        EnableDisablePluginStepSchema.shape,
        async (params: z.infer<typeof EnableDisablePluginStepSchema>) => {
          await client.update("sdkmessageprocessingsteps", params.stepId, {
            statecode: params.enabled ? 0 : 1,
            statuscode: params.enabled ? 1 : 2,
          });
    
          return {
            content: [
              {
                type: "text" as const,
                text: `Step ${params.stepId} ${params.enabled ? "ativado" : "desativado"} com sucesso!`,
              },
            ],
          };
        }
      );
    
      // 7. Delete Plugin Step
      server.tool(
        "dynamics_delete_plugin_step",
        "Remove um step de plugin registrado",
        DeletePluginStepSchema.shape,
        async (params: z.infer<typeof DeletePluginStepSchema>) => {
          await client.remove("sdkmessageprocessingsteps", params.stepId);
          return {
            content: [
              {
                type: "text" as const,
                text: `Step ${params.stepId} removido com sucesso!`,
              },
            ],
          };
        }
      );
    
      // 8. Get Plugin Trace Logs
      server.tool(
        "dynamics_get_plugin_trace_logs",
        "Recupera logs de rastreamento de plugins para diagnóstico",
        GetPluginTraceLogsSchema.shape,
        async (params: z.infer<typeof GetPluginTraceLogsSchema>) => {
          const filters: string[] = [];
          if (params.pluginTypeName) {
            filters.push(`contains(typename,'${params.pluginTypeName}')`);
          }
          if (params.correlationId) {
            filters.push(`correlationid eq '${params.correlationId}'`);
          }
    
          const result = await client.list("plugintracelogs", {
            select: [
              "plugintracelogid", "typename", "messagename", "performanceexecutionstarttime",
              "performanceexecutionduration", "operationtype", "messageblock",
              "exceptiondetails", "correlationid", "depth", "createdon",
            ],
            filter: filters.length > 0 ? filters.join(" and ") : undefined,
            orderby: "createdon desc",
            top: params.top,
          });
    
          return {
            content: [
              {
                type: "text" as const,
                text: `Logs de plugin encontrados: ${result.value.length}\n\n${JSON.stringify(result.value, null, 2)}`,
              },
            ],
          };
        }
      );
    
      // 9. Generate Plugin Project
      server.tool(
        "dynamics_generate_plugin_project",
        "Gera a estrutura completa de um projeto de plugin C# (.csproj, classes, etc.)",
        z.object({
          projectName: z.string().describe("Nome do projeto"),
          namespace: z.string().describe("Namespace do projeto"),
          plugins: z.array(CreatePluginSchema).describe("Lista de plugins a serem gerados"),
        }).shape,
        async (params: { projectName: string; namespace: string; plugins: z.infer<typeof CreatePluginSchema>[] }) => {
          const csproj = PLUGIN_TEMPLATES.csproj
            .replace(/{{PROJECT_NAME}}/g, params.projectName)
            .replace(/{{NAMESPACE}}/g, params.namespace);
    
          const pluginFiles: string[] = [];
          for (const plugin of params.plugins) {
            const stageValue = STAGE_MAP[plugin.stage] || 40;
            const code = PLUGIN_TEMPLATES.standard
              .replace(/{{NAMESPACE}}/g, params.namespace)
              .replace(/{{CLASS_NAME}}/g, plugin.name)
              .replace(/{{ENTITY_LOGICAL_NAME}}/g, plugin.entityLogicalName)
              .replace(/{{MESSAGE}}/g, plugin.message)
              .replace(/{{STAGE}}/g, String(stageValue))
              .replace(/{{EXECUTION_MODE}}/g, plugin.executionMode === "Synchronous" ? "0" : "1")
              .replace(/{{FILTERING_ATTRIBUTES}}/g, plugin.filteringAttributes || "")
              .replace(/{{DESCRIPTION}}/g, plugin.description || `Plugin ${plugin.name}`)
              .replace(/{{BUSINESS_LOGIC}}/g, plugin.businessLogic || "// TODO: Implement business logic");
            pluginFiles.push(`// File: ${plugin.name}.cs\n${code}`);
          }
    
          return {
            content: [
              {
                type: "text" as const,
                text: `Projeto gerado: **${params.projectName}**\n\n## ${params.projectName}.csproj\n\`\`\`xml\n${csproj}\n\`\`\`\n\n## Plugins\n${pluginFiles.map((f) => `\`\`\`csharp\n${f}\n\`\`\``).join("\n\n")}`,
              },
            ],
          };
        }
      );
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden for behavioral disclosure. The description only states what the tool does ('Lista os assemblies'), but doesn't mention whether this is a read-only operation, what permissions might be required, whether results are paginated, or what format the output takes. For a listing tool with zero annotation coverage, this is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence in Portuguese that clearly states the tool's purpose. There's no wasted language or unnecessary elaboration - every word contributes to understanding what the tool does.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a listing tool with no annotations and no output schema, the description is too minimal. It doesn't explain what information is returned about plugin assemblies, whether results are filtered or paginated, or what the typical use cases are. Given the context of multiple similar listing tools in the sibling set, more differentiation would be helpful.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents both parameters (assemblyName and entityLogicalName) with descriptions in Portuguese. The tool description doesn't add any parameter information beyond what's in the schema, but since schema coverage is complete, the baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('Lista' - lists) and resource ('assemblies de plugins registrados no Dynamics CRM'), making the purpose specific and understandable. However, it doesn't explicitly differentiate from sibling tools like 'dynamics_list_plugin_steps' or 'dynamics_list_plugin_types', which are related but distinct listing operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With multiple sibling listing tools (e.g., dynamics_list_plugin_steps, dynamics_list_plugin_types, dynamics_list_solutions), there's no indication of what makes this tool distinct or when it should be preferred over other listing operations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rafteles2016/mcpDynamics'

If you have feedback or need assistance with the MCP directory API, please join our Discord server