Skip to main content
Glama
metrxbots

Metrx MCP Server

by metrxbots

metrx_get_attribution_report

Analyze which agent actions drive business outcomes by generating attribution reports with outcome counts, values, confidence scores, and top contributors.

Instructions

Get attribution report showing which agent actions led to business outcomes. Shows outcome counts, total values, confidence scores, and top contributing agents. Do NOT use for board-level reporting — use generate_roi_audit for formal audit reports.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
agent_idNoOptional: filter to specific agent (omit for fleet-wide)
daysNoNumber of days to include (default: 30)
modelNoAttribution model to use (default: direct)direct

Implementation Reference

  • The handler function for get_attribution_report that fetches attribution data from the API and formats it as a report showing outcome counts, values, confidence scores, and top contributing agents.
    async ({ agent_id, days, model }) => {
      const params: Record<string, string | number> = {
        days: days ?? 30,
        model: model ?? 'direct',
      };
      if (agent_id) params.agent_id = agent_id;
    
      const result = await client.get<AttributionReportResponse>('/outcomes', params);
    
      if (result.error) {
        return {
          content: [{ type: 'text', text: `Error fetching attribution report: ${result.error}` }],
          isError: true,
        };
      }
    
      const data = result.data!;
      const lines: string[] = [
        '## Attribution Report',
        `### Period: Last ${data.period_days} days | Model: ${data.model}`,
        '',
      ];
    
      if (data.agent_id) {
        lines.push(`**Agent**: ${data.agent_id}`, '');
      } else {
        lines.push('**Scope**: Fleet-wide (all agents)', '');
      }
    
      lines.push(`**Total Outcomes**: ${data.total_outcomes}`, '');
      const totalRevenue = (data.total_value_cents / 100).toFixed(2);
      lines.push(`**Total Value**: $${totalRevenue}`, '');
    
      if (data.outcomes.length === 0) {
        lines.push('No outcomes recorded in this period.');
        return {
          content: [{ type: 'text', text: lines.join('\n') }],
        };
      }
    
      lines.push('', '### Outcome Breakdown');
      for (const outcome of data.outcomes) {
        lines.push('', `#### ${outcome.outcome_type}`);
        lines.push(`- **Count**: ${outcome.count}`);
        const value = (outcome.value_cents / 100).toFixed(2);
        lines.push(`- **Total Value**: $${value}`);
        lines.push(`- **Confidence**: ${(outcome.confidence * 100).toFixed(0)}%`);
    
        if (outcome.top_attributions && outcome.top_attributions.length > 0) {
          lines.push('- **Top Agents**:');
          for (const attr of outcome.top_attributions) {
            const attrValue = (attr.contribution_value_cents / 100).toFixed(2);
            const attrConf = (attr.confidence * 100).toFixed(0);
            lines.push(`  - ${attr.agent_name} [${attr.agent_id}]: $${attrValue} (${attrConf}%)`);
          }
        }
      }
    
      return {
        content: [{ type: 'text', text: lines.join('\n') }],
      };
    }
  • Tool registration for get_attribution_report with title, description, input schema, annotations, and handler function. The tool is registered without prefix here but gets 'metrx_' added in index.ts.
    // ── get_attribution_report ──
    server.registerTool(
      'get_attribution_report',
      {
        title: 'Get Attribution Report',
        description:
          'Get attribution report showing which agent actions led to business outcomes. ' +
          'Shows outcome counts, total values, confidence scores, and top contributing agents. ' +
          'Do NOT use for board-level reporting — use generate_roi_audit for formal audit reports.',
        inputSchema: {
          agent_id: z
            .string()
            .uuid()
            .optional()
            .describe('Optional: filter to specific agent (omit for fleet-wide)'),
          days: z
            .number()
            .int()
            .min(1)
            .max(365)
            .default(30)
            .describe('Number of days to include (default: 30)'),
          model: z
            .enum(['direct', 'last_touch', 'first_touch'])
            .default('direct')
            .describe('Attribution model to use (default: direct)'),
        },
        annotations: {
          readOnlyHint: true,
          destructiveHint: false,
          idempotentHint: true,
          openWorldHint: false,
        },
      },
      async ({ agent_id, days, model }) => {
        const params: Record<string, string | number> = {
          days: days ?? 30,
          model: model ?? 'direct',
        };
        if (agent_id) params.agent_id = agent_id;
    
        const result = await client.get<AttributionReportResponse>('/outcomes', params);
    
        if (result.error) {
          return {
            content: [{ type: 'text', text: `Error fetching attribution report: ${result.error}` }],
            isError: true,
          };
        }
    
        const data = result.data!;
        const lines: string[] = [
          '## Attribution Report',
          `### Period: Last ${data.period_days} days | Model: ${data.model}`,
          '',
        ];
    
        if (data.agent_id) {
          lines.push(`**Agent**: ${data.agent_id}`, '');
        } else {
          lines.push('**Scope**: Fleet-wide (all agents)', '');
        }
    
        lines.push(`**Total Outcomes**: ${data.total_outcomes}`, '');
        const totalRevenue = (data.total_value_cents / 100).toFixed(2);
        lines.push(`**Total Value**: $${totalRevenue}`, '');
    
        if (data.outcomes.length === 0) {
          lines.push('No outcomes recorded in this period.');
          return {
            content: [{ type: 'text', text: lines.join('\n') }],
          };
        }
    
        lines.push('', '### Outcome Breakdown');
        for (const outcome of data.outcomes) {
          lines.push('', `#### ${outcome.outcome_type}`);
          lines.push(`- **Count**: ${outcome.count}`);
          const value = (outcome.value_cents / 100).toFixed(2);
          lines.push(`- **Total Value**: $${value}`);
          lines.push(`- **Confidence**: ${(outcome.confidence * 100).toFixed(0)}%`);
    
          if (outcome.top_attributions && outcome.top_attributions.length > 0) {
            lines.push('- **Top Agents**:');
            for (const attr of outcome.top_attributions) {
              const attrValue = (attr.contribution_value_cents / 100).toFixed(2);
              const attrConf = (attr.confidence * 100).toFixed(0);
              lines.push(`  - ${attr.agent_name} [${attr.agent_id}]: $${attrValue} (${attrConf}%)`);
            }
          }
        }
    
        return {
          content: [{ type: 'text', text: lines.join('\n') }],
        };
      }
    );
  • Input schema defining the parameters for get_attribution_report: optional agent_id (UUID), days (1-365, default 30), and model (enum: 'direct', 'last_touch', 'first_touch', default 'direct').
    inputSchema: {
      agent_id: z
        .string()
        .uuid()
        .optional()
        .describe('Optional: filter to specific agent (omit for fleet-wide)'),
      days: z
        .number()
        .int()
        .min(1)
        .max(365)
        .default(30)
        .describe('Number of days to include (default: 30)'),
      model: z
        .enum(['direct', 'last_touch', 'first_touch'])
        .default('direct')
        .describe('Attribution model to use (default: direct)'),
    },
  • Type definition for AttributionReportResponse which defines the structure of the API response including agent_id, period_days, model, total_outcomes, total_value_cents, and outcomes array with breakdown by type.
    interface AttributionReportResponse {
      agent_id?: string;
      period_days: number;
      model: string;
      total_outcomes: number;
      total_value_cents: number;
      outcomes: Array<{
        outcome_type: string;
        count: number;
        value_cents: number;
        confidence: number;
        top_attributions: Array<{
          agent_id: string;
          agent_name: string;
          contribution_value_cents: number;
          confidence: number;
        }>;
      }>;
    }
  • src/index.ts:74-103 (registration)
    The wrapper function that adds the 'metrx_' prefix to all registered tools. This transforms 'get_attribution_report' into 'metrx_get_attribution_report' and also wraps handlers with rate limiting.
    // ── Rate limiting middleware + metrx_ namespace prefix ──
    // All tools are registered exclusively as metrx_{name}.
    // The metrx_ prefix namespaces our tools to avoid collisions when
    // multiple MCP servers are used together.
    const METRX_PREFIX = 'metrx_';
    const originalRegisterTool = server.registerTool.bind(server);
    (server as any).registerTool = function (
      name: string,
      config: any,
      handler: (...handlerArgs: any[]) => Promise<any>
    ) {
      const wrappedHandler = async (...handlerArgs: any[]) => {
        if (!rateLimiter.isAllowed(name)) {
          return {
            content: [
              {
                type: 'text' as const,
                text: `Rate limit exceeded for tool '${name}'. Maximum 60 requests per minute allowed.`,
              },
            ],
            isError: true,
          };
        }
        return handler(...handlerArgs);
      };
    
      // Register with metrx_ prefix (only — no deprecated aliases)
      const prefixedName = name.startsWith(METRX_PREFIX) ? name : `${METRX_PREFIX}${name}`;
      originalRegisterTool(prefixedName, config, wrappedHandler);
    };

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/metrxbots/metrx-mcp-server'

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