Skip to main content
Glama

Simulation Data

simulation_data
Read-only

Fetch simulation data: agent profiles, configuration, action logs, social media posts, round-by-round timeline, per-agent activity stats, and interview history. Paginated—use offset to get more results when has_more is true.

Instructions

Access simulation data: agent profiles, configuration, action logs, social media posts, round-by-round timeline, per-agent activity stats, and interview history. Paginated — use offset to get more results when has_more is true.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
simulation_idYesThe simulation ID
data_typeYesWhat data to retrieve: overview (condensed summary: entities, agents, graph, config, action stats — start here), profiles (full agent personas), config (simulation parameters), actions (agent action log), posts (social media posts from SQLite), timeline (per-round summaries), agent_stats (per-agent activity breakdown), interview_history (past interview transcripts)
platformNoFilter by platform (for actions and posts)
agent_nameNoFilter actions by agent name
action_typeNoFilter actions by type (CREATE_POST, LIKE_POST, etc.)
limitNoMax results per page (default 50)
offsetNoOffset for pagination (default 0)

Implementation Reference

  • Registration of the 'simulation_data' tool on the MCP server with its input schema and handler callback
    export function registerSimulationData(server: McpServer, client: MirofishClient): void {
      server.registerTool(
        "simulation_data",
        {
          title: "Simulation Data",
          description:
            "Access simulation data: agent profiles, configuration, action logs, " +
            "social media posts, round-by-round timeline, per-agent activity stats, " +
            "and interview history. Paginated — use offset to get more results when has_more is true.",
          inputSchema,
          annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
        },
        async (args) => {
          try {
            let data: unknown;
            const limit = args.limit ?? PAGE_SIZE;
            const offset = args.offset ?? 0;
    
            switch (args.data_type) {
              case "overview":
                data = await buildOverview(client, args.simulation_id);
                break;
    
              case "profiles": {
                const all = await client.getSimulationProfiles(args.simulation_id);
                data = paginate(all as any[], limit, offset);
                break;
              }
    
              case "config":
                data = await client.getSimulationConfig(args.simulation_id);
                break;
    
              case "actions": {
                const resp = await client.getSimulationActions(args.simulation_id, {
                  platform: args.platform,
                  limit: 500, // fetch more, paginate client-side
                });
                let items = resp.actions as any[];
                if (args.agent_name) {
                  const q = args.agent_name.toLowerCase();
                  items = items.filter((a) =>
                    String(a.agent_name ?? "").toLowerCase().includes(q),
                  );
                }
                if (args.action_type) {
                  items = items.filter((a) => a.action_type === args.action_type);
                }
                data = paginate(items, limit, offset);
                break;
              }
    
              case "posts": {
                data = await client.getSimulationPosts(args.simulation_id, {
                  limit,
                  offset,
                });
                break;
              }
    
              case "timeline": {
                const all = await client.getSimulationTimeline(args.simulation_id);
                data = paginate(all as any[], limit, offset);
                break;
              }
    
              case "agent_stats":
                data = await client.getAgentStats(args.simulation_id);
                break;
    
              case "interview_history": {
                const all = await client.getInterviewHistory(args.simulation_id);
                data = paginate(all as any[], limit, offset);
                break;
              }
            }
    
            return {
              content: [
                {
                  type: "text" as const,
                  text: JSON.stringify(data, null, 2),
                },
              ],
            };
          } catch (err) {
            throw toMcpError(err);
          }
        },
      );
  • Input schema using Zod defining simulation_id, data_type (8 variants), platform, agent_name, action_type, limit, and offset fields
    const inputSchema = {
      simulation_id: z.string().describe("The simulation ID"),
      data_type: z
        .enum(["overview", "profiles", "config", "actions", "posts", "timeline", "agent_stats", "interview_history"])
        .describe(
          "What data to retrieve: " +
          "overview (condensed summary: entities, agents, graph, config, action stats — start here), " +
          "profiles (full agent personas), " +
          "config (simulation parameters), " +
          "actions (agent action log), " +
          "posts (social media posts from SQLite), " +
          "timeline (per-round summaries), " +
          "agent_stats (per-agent activity breakdown), " +
          "interview_history (past interview transcripts)",
        ),
      platform: z
        .enum(["twitter", "reddit"])
        .optional()
        .describe("Filter by platform (for actions and posts)"),
      agent_name: z.string().optional().describe("Filter actions by agent name"),
      action_type: z.string().optional().describe("Filter actions by type (CREATE_POST, LIKE_POST, etc.)"),
      limit: z.coerce.number().int().min(1).max(200).optional().describe("Max results per page (default 50)"),
      offset: z.coerce.number().int().min(0).optional().describe("Offset for pagination (default 0)"),
    };
  • Handler implementation: dispatches on data_type to fetch overview (aggregated summary), profiles, config, actions, posts, timeline, agent_stats, or interview_history from the backend client, with pagination support
    async (args) => {
      try {
        let data: unknown;
        const limit = args.limit ?? PAGE_SIZE;
        const offset = args.offset ?? 0;
    
        switch (args.data_type) {
          case "overview":
            data = await buildOverview(client, args.simulation_id);
            break;
    
          case "profiles": {
            const all = await client.getSimulationProfiles(args.simulation_id);
            data = paginate(all as any[], limit, offset);
            break;
          }
    
          case "config":
            data = await client.getSimulationConfig(args.simulation_id);
            break;
    
          case "actions": {
            const resp = await client.getSimulationActions(args.simulation_id, {
              platform: args.platform,
              limit: 500, // fetch more, paginate client-side
            });
            let items = resp.actions as any[];
            if (args.agent_name) {
              const q = args.agent_name.toLowerCase();
              items = items.filter((a) =>
                String(a.agent_name ?? "").toLowerCase().includes(q),
              );
            }
            if (args.action_type) {
              items = items.filter((a) => a.action_type === args.action_type);
            }
            data = paginate(items, limit, offset);
            break;
          }
    
          case "posts": {
            data = await client.getSimulationPosts(args.simulation_id, {
              limit,
              offset,
            });
            break;
          }
    
          case "timeline": {
            const all = await client.getSimulationTimeline(args.simulation_id);
            data = paginate(all as any[], limit, offset);
            break;
          }
    
          case "agent_stats":
            data = await client.getAgentStats(args.simulation_id);
            break;
    
          case "interview_history": {
            const all = await client.getInterviewHistory(args.simulation_id);
            data = paginate(all as any[], limit, offset);
            break;
          }
        }
    
        return {
          content: [
            {
              type: "text" as const,
              text: JSON.stringify(data, null, 2),
            },
          ],
        };
      } catch (err) {
        throw toMcpError(err);
      }
    },
  • Helper function that builds the 'overview' data type by aggregating status, profiles, config, agent stats, timeline, and knowledge graph into a condensed summary
    async function buildOverview(client: MirofishClient, simulationId: string): Promise<Record<string, unknown>> {
      const [sim, profiles, config, agentStats, timeline] = await Promise.all([
        client.getStatus(simulationId).catch(() => null),
        client.getSimulationProfiles(simulationId).catch(() => []),
        client.getSimulationConfig(simulationId).catch(() => ({})),
        client.getAgentStats(simulationId).catch(() => ({})),
        client.getSimulationTimeline(simulationId).catch(() => []),
      ]);
    
      // Fetch knowledge graph if graph_id is available
      const graphId = (sim as any)?.graph_id;
      let knowledgeGraph: { entities: number; relations: number; top_entities: any[] } | null = null;
      if (graphId) {
        try {
          const gd = await client.getGraphData(graphId) as any;
          const nodes = gd?.nodes ?? [];
          const edges = gd?.edges ?? [];
          knowledgeGraph = {
            entities: nodes.length,
            relations: edges.length,
            top_entities: nodes.slice(0, 15).map((n: any) => ({
              name: n.name,
              type: n.entity_type || n.type,
              summary: n.summary?.slice(0, 100) || "",
            })),
          };
        } catch { /* graph not available */ }
      }
    
      // Condense profiles to name + type + stance (first 50 only)
      const allAgents = (profiles as any[]).map((p: any, i: number) => ({
        id: i,
        name: p.realname || p.name || p.username || `Agent ${i}`,
        type: p.entity_type || p.profession || "Unknown",
        stance: p.stance || p.persona?.slice(0, 80) || "",
      }));
    
      const timeConfig = (config as any)?.time_config;
      const entityTypes = (config as any)?.entity_types || (sim as any)?.entity_types || [];
    
      // Condense timeline to 5 snapshots
      const tl = timeline as any[];
      const timelineSnapshot = tl.length <= 5 ? tl : [
        tl[0],
        tl[Math.floor(tl.length / 4)],
        tl[Math.floor(tl.length / 2)],
        tl[Math.floor(tl.length * 3 / 4)],
        tl[tl.length - 1],
      ];
    
      return {
        simulation_id: simulationId,
        status: (sim as any)?.status ?? "unknown",
        entity_types: entityTypes,
        agents_count: allAgents.length,
        agents: allAgents.slice(0, PAGE_SIZE),
        agents_has_more: allAgents.length > PAGE_SIZE,
        simulation_config: timeConfig ? {
          total_hours: timeConfig.total_simulation_hours,
          minutes_per_round: timeConfig.minutes_per_round,
          platforms: [(sim as any)?.enable_twitter && "twitter", (sim as any)?.enable_reddit && "reddit"].filter(Boolean),
        } : null,
        knowledge_graph: knowledgeGraph,
        activity: agentStats,
        timeline_snapshot: timelineSnapshot,
      };
    }
  • MirofishClient helper methods that make HTTP calls to the backend API endpoints for simulation data (profiles, actions, posts, timeline, agent-stats, config, graph data, interview history)
      // ------------------------------------------------------------------
      // Simulation data (actions, posts, profiles, timeline, stats)
      // ------------------------------------------------------------------
    
      async getSimulationProfiles(simulationId: string): Promise<unknown[]> {
        const resp = await this.http.get<MirofishApiResponse<unknown[]>>(
          `/api/simulation/${simulationId}/profiles`,
        );
        return (resp.data?.data as unknown[]) ?? [];
      }
    
      async getSimulationActions(
        simulationId: string,
        params: { limit?: number; offset?: number; platform?: string; agent_id?: number; round_num?: number } = {},
      ): Promise<{ count: number; actions: unknown[] }> {
        const resp = await this.http.get<MirofishApiResponse<{ count: number; actions: unknown[] }>>(
          `/api/simulation/${simulationId}/actions`,
          { params },
        );
        return resp.data?.data ?? { count: 0, actions: [] };
      }
    
      async getSimulationPosts(
        simulationId: string,
        params: { limit?: number; offset?: number } = {},
      ): Promise<{ posts: Array<{ user_id: number; content: string; num_likes?: number }>; total: number }> {
        const resp = await this.http.get<
          MirofishApiResponse<{ posts: Array<{ user_id: number; content: string; num_likes?: number }>; total: number }>
        >(`/api/simulation/${simulationId}/posts`, { params });
        return resp.data?.data ?? { posts: [], total: 0 };
      }
    
      async getSimulationTimeline(simulationId: string): Promise<unknown[]> {
        const resp = await this.http.get<MirofishApiResponse<{ timeline: unknown[] }>>(
          `/api/simulation/${simulationId}/timeline`,
        );
        return resp.data?.data?.timeline ?? [];
      }
    
      async getAgentStats(simulationId: string): Promise<Record<string, unknown>> {
        const resp = await this.http.get<MirofishApiResponse<{ stats: Record<string, unknown> }>>(
          `/api/simulation/${simulationId}/agent-stats`,
        );
        return resp.data?.data?.stats ?? {};
      }
    
      async getSimulationConfig(simulationId: string): Promise<Record<string, unknown>> {
        const resp = await this.http.get<MirofishApiResponse<Record<string, unknown>>>(
          `/api/simulation/${simulationId}/config`,
        );
        return resp.data?.data ?? {};
      }
    
      async getGraphData(graphId: string): Promise<Record<string, unknown>> {
        const resp = await this.http.get<MirofishApiResponse<Record<string, unknown>>>(
          `/api/graph/data/${graphId}`,
        );
        return resp.data?.data ?? {};
      }
    
      async getInterviewHistory(
        simulationId: string,
        agentId?: number,
      ): Promise<unknown[]> {
        const resp = await this.http.post<MirofishApiResponse<unknown[]>>(
          `/api/simulation/interview/history`,
          { simulation_id: simulationId, agent_id: agentId },
        );
        return (resp.data?.data as unknown[]) ?? [];
      }
    
      // ------------------------------------------------------------------
      // Legacy pipeline helpers (private, used only by _legacyCreateAndRun)
      // ------------------------------------------------------------------
    
      private async _legacyGenerateOntology(
        prompt: string,
        documentId?: string,
      ): Promise<{ project_id: string }> {
        const resp = await this.http.post<MirofishApiResponse<{ project_id: string }>>(
          "/api/graph/ontology/generate",
          { simulation_goal: prompt, document_id: documentId },
        );
        if (!resp.data?.data?.project_id) {
          throw new MirofishBackendError("Ontology generation failed", 500);
        }
        return resp.data.data;
      }
    
      private async _legacyBuildGraph(projectId: string): Promise<{ task_id: string }> {
        const resp = await this.http.post<MirofishApiResponse<{ task_id: string }>>(
          "/api/graph/build",
          { project_id: projectId },
        );
        if (!resp.data?.data?.task_id) {
          throw new MirofishBackendError("Graph build failed to start", 500);
        }
        return resp.data.data;
      }
    
      private async _legacyGetProject(projectId: string): Promise<{ graph_id: string }> {
        const resp = await this.http.get<MirofishApiResponse<{ graph_id: string }>>(
          `/api/graph/project/${projectId}`,
        );
        if (!resp.data?.data?.graph_id) {
          throw new MirofishBackendError("Project has no graph_id", 500);
        }
        return resp.data.data;
      }
    
      private async _legacyReadDocumentText(documentId: string): Promise<string> {
        try {
          const resp = await this.http.get<MirofishApiResponse<{ text: string }>>(
            `/api/documents/${documentId}`,
          );
          return resp.data?.data?.text ?? "";
        } catch {
          return "";
        }
      }
    
      private async _legacyPollTask(taskId: string, timeoutMs = 600_000): Promise<void> {
        const start = Date.now();
        while (Date.now() - start < timeoutMs) {
          const resp = await this.http.get<MirofishApiResponse<{ status: string; error?: string }>>(
            `/api/tasks/${taskId}`,
          );
          const data = resp.data?.data;
          if (data?.status === "completed") return;
          if (data?.status === "failed") {
            throw new MirofishBackendError(`Task failed: ${data.error ?? "unknown"}`, 500);
          }
          await new Promise((r) => setTimeout(r, 2000));
        }
        throw new MirofishBackendError("Task timed out", 504);
      }
    
      private _resolveRounds(preset?: string): number {
        switch (preset) {
          case "quick": return 20;
          case "deep": return 72;
          case "standard":
          default: return 40;
        }
      }
    }
Behavior4/5

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

Annotations already indicate read-only and non-destructive behavior. The description adds value by explaining pagination (offset and has_more) and notes that overview is a condensed summary to start with, providing behavioral context beyond annotations.

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 sentence followed by a brief note on pagination. No extraneous information; every sentence earns its place.

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

Completeness4/5

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

Given the tool's complexity (7 parameters, no output schema), the description combined with detailed schema descriptions is largely sufficient. It could mention error handling or performance, but the pagination note and data type advice cover key usage aspects.

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

Parameters4/5

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

With 100% schema coverage, the schema already describes parameters well. The description adds useful context by explaining pagination and advising to start with 'overview' for data_type, going beyond the schema.

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

Purpose5/5

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

The description clearly states the tool accesses simulation data and enumerates specific data types (agent profiles, config, etc.). It distinguishes itself from sibling tools like create_simulation or get_report by focusing on raw data access, not creation or summary reports.

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

Usage Guidelines3/5

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

The description provides no explicit guidance on when to use this tool versus alternatives like get_report or interview_agent. Pagination usage is mentioned but context for choosing data types or comparing with other tools is missing.

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/kakarot-dev/deepmiro'

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