Skip to main content
Glama

proxy_export_har

Export recorded proxy sessions to HAR format for analysis, supporting filtering by method, URL, status code, and time range to capture specific network traffic.

Instructions

Export a recorded session (or filtered subset) to HAR format.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
session_idYesSession ID
output_fileNoOutput HAR file path
include_bodiesNoInclude body text when available
methodNo
hostname_containsNo
url_containsNo
status_codeNo
from_tsNo
to_tsNo
textNo
sortNoasc

Implementation Reference

  • The proxyManager.exportSessionHar method acts as a wrapper that calls the SessionStore.exportHar method to perform the actual HAR export logic.
    // ── ProxyManager ──
    
    let nextRuleId = 1;
    
    export class ProxyManager {
      private server: mockttp.Mockttp | null = null;
      private cert: CertificateInfo | null = null;
      private port: number | null = null;
      private _running = false;
      private readonly sessionStore = new SessionStore();
    
      // Upstream proxy
      private globalUpstream: UpstreamProxyConfig | null = null;
      private hostUpstreams = new Map<string, UpstreamProxyConfig>();
    
      // Interception rules
      private rules = new Map<string, InterceptionRule>();
    
      // Traffic capture (ring buffer)
      private traffic: CapturedExchange[] = [];
      private pendingRequests = new Map<string, CapturedExchange>();
      private pendingRawBodies = new Map<string, { requestBody?: Buffer }>();
    
      // TLS fingerprinting
      private tlsMetadataCache = new Map<string, TlsClientMetadata>();
      private serverTlsCapture: ServerTlsCapture | null = null;
      private _ja3SpoofConfig: FingerprintSpoofConfig | null = null;
    
      // ── Lifecycle ──
    
      async start(port?: number, options: ProxyStartOptions = {}): Promise<{ port: number; url: string; cert: CertificateInfo }> {
        if (this._running) {
          throw new Error("Proxy is already running. Stop it first.");
        }
    
        // Generate CA cert (once, reused across rebuilds)
        if (!this.cert) {
          const mockttp = await getMockttp();
          const ca = await mockttp.generateCACertificate({ bits: 2048 });
          const fingerprint = mockttp.generateSPKIFingerprint(ca.cert);
          this.cert = { key: ca.key, cert: ca.cert, fingerprint };
        }
    
        this.port = port || 0;
        await this.buildAndStart();
        this._running = true;
        this.port = this.server!.port;
    
        if (options.persistenceEnabled) {
          await this.sessionStore.startSession({
            sessionName: options.sessionName,
            captureProfile: options.captureProfile,
            storageDir: options.storageDir,
            maxDiskMb: options.maxDiskMb,
          });
        }
    
        return {
          port: this.server!.port,
          url: this.server!.url,
          cert: this.cert,
        };
      }
    
      async stop(): Promise<void> {
        if (!this._running) {
          throw new Error("Proxy is not running.");
        }
        // Deactivate all interceptors before stopping the proxy
        await interceptorManager.deactivateAll().catch(() => {});
        await cleanupTempCerts().catch(() => {});
        if (this.server) {
          await this.server.stop();
          this.server = null;
        }
        await this.sessionStore.stopSession().catch(() => {});
        this._running = false;
        this.pendingRequests.clear();
        this.pendingRawBodies.clear();
        this.tlsMetadataCache.clear();
        this.disableServerTls();
        if (this._ja3SpoofConfig) {
          await shutdownSpoofContainer();
        }
      }
    
      isRunning(): boolean {
        return this._running;
      }
    
      getPort(): number | null {
        return this.port;
      }
    
      getCert(): CertificateInfo | null {
        return this.cert;
      }
    
      getStatus(): object {
        return {
          running: this.isRunning(),
          port: this.port,
          url: this.server?.url ?? null,
          certFingerprint: this.cert?.fingerprint ?? null,
          globalUpstream: this.globalUpstream,
          hostUpstreams: Object.fromEntries(this.hostUpstreams),
          ruleCount: this.rules.size,
          trafficCount: this.traffic.length,
          persistence: this.sessionStore.getRuntimeStatus(),
        };
      }
    
      // ── Upstream Proxy ──
    
      async setGlobalUpstream(config: UpstreamProxyConfig): Promise<void> {
        this.globalUpstream = config;
        if (this._running) await this.rebuildMockttpRules();
      }
    
      async clearGlobalUpstream(): Promise<void> {
        this.globalUpstream = null;
        if (this._running) await this.rebuildMockttpRules();
      }
    
      getGlobalUpstream(): UpstreamProxyConfig | null {
        return this.globalUpstream;
      }
    
      async setHostUpstream(hostname: string, config: UpstreamProxyConfig): Promise<void> {
        this.hostUpstreams.set(hostname, config);
        if (this._running) await this.rebuildMockttpRules();
      }
    
      async removeHostUpstream(hostname: string): Promise<boolean> {
        const removed = this.hostUpstreams.delete(hostname);
        if (removed && this._running) await this.rebuildMockttpRules();
        return removed;
      }
    
      getHostUpstreams(): Map<string, UpstreamProxyConfig> {
        return this.hostUpstreams;
      }
    
      // ── Interception Rules ──
    
      async addRule(rule: Omit<InterceptionRule, "id" | "hitCount" | "createdAt">): Promise<InterceptionRule> {
        const id = `rule_${nextRuleId++}`;
        const newRule: InterceptionRule = {
          ...rule,
          id,
          hitCount: 0,
          createdAt: Date.now(),
        };
        this.rules.set(id, newRule);
        if (this._running) await this.rebuildMockttpRules();
        return newRule;
      }
    
      async updateRule(id: string, updates: Partial<Omit<InterceptionRule, "id" | "hitCount" | "createdAt">>): Promise<InterceptionRule> {
        const rule = this.rules.get(id);
        if (!rule) throw new Error(`Rule '${id}' not found`);
        Object.assign(rule, updates);
        if (this._running) await this.rebuildMockttpRules();
        return rule;
      }
    
      async removeRule(id: string): Promise<boolean> {
        const removed = this.rules.delete(id);
        if (removed && this._running) await this.rebuildMockttpRules();
        return removed;
      }
    
      getRule(id: string): InterceptionRule | undefined {
        return this.rules.get(id);
      }
    
      listRules(): InterceptionRule[] {
        return [...this.rules.values()].sort((a, b) => a.priority - b.priority);
      }
    
      async enableRule(id: string): Promise<void> {
        const rule = this.rules.get(id);
        if (!rule) throw new Error(`Rule '${id}' not found`);
        rule.enabled = true;
        if (this._running) await this.rebuildMockttpRules();
      }
    
      async disableRule(id: string): Promise<void> {
        const rule = this.rules.get(id);
        if (!rule) throw new Error(`Rule '${id}' not found`);
        rule.enabled = false;
        if (this._running) await this.rebuildMockttpRules();
      }
    
      testRulesAgainstRequest(
        requestInput: { url: string } & Partial<Omit<RuleTestRequest, "url">>,
        options: RuleTestOptions = {},
      ): RuleTestResult {
        const request = this.normalizeRuleTestRequest(requestInput);
        const includeDisabled = options.includeDisabled ?? true;
        const limitRules = options.limitRules;
    
        const allRules = this.listRules();
        const candidates = includeDisabled ? allRules : allRules.filter((r) => r.enabled);
        const rules = (typeof limitRules === "number" && limitRules >= 0)
          ? candidates.slice(0, limitRules)
          : candidates;
    
        const results: RuleMatchEvaluation[] = rules.map((rule) => {
          const { matched, checks } = this.evaluateRuleMatcher(rule.matcher, request);
          const eligible = matched && rule.enabled;
          return {
            ruleId: rule.id,
            enabled: rule.enabled,
            priority: rule.priority,
            description: rule.description,
            matched,
            eligible,
            checks,
          };
        });
    
        const matchedCount = results.filter((r) => r.matched).length;
        const effectiveMatches = results.filter((r) => r.eligible);
        const winner = effectiveMatches[0] ?? null;
        const winnerRule = winner ? this.getRule(winner.ruleId) ?? null : null;
    
        return {
          request,
          includeDisabled,
          evaluatedCount: results.length,
          totalRules: allRules.length,
          results,
          matchedCount,
          effectiveMatchCount: effectiveMatches.length,
          effectiveWinner: winner && winnerRule ? {
            ruleId: winner.ruleId,
            priority: winner.priority,
            description: winner.description,
            handlerType: winnerRule.handler.type,
          } : null,
        };
      }
    
      testRulesAgainstExchange(exchangeId: string, options: RuleTestOptions = {}): RuleTestResult {
        const exchange = this.getExchange(exchangeId);
        if (!exchange) {
          throw new Error(`Exchange '${exchangeId}' not found`);
        }
        return this.testRulesAgainstRequest({
          method: exchange.request.method,
          url: exchange.request.url,
          hostname: exchange.request.hostname,
          path: exchange.request.path,
          headers: exchange.request.headers,
          body: exchange.request.bodyPreview,
        }, options);
      }
    
      // ── Traffic ──
    
      getTraffic(): CapturedExchange[] {
        return this.traffic;
      }
    
      getExchange(id: string): CapturedExchange | undefined {
        return this.traffic.find((t) => t.id === id);
      }
    
      searchTraffic(query: string): CapturedExchange[] {
        const q = query.toLowerCase();
        return this.traffic.filter((t) => {
          if (t.request.url.toLowerCase().includes(q)) return true;
          if (t.request.bodyPreview.toLowerCase().includes(q)) return true;
          if (t.response?.bodyPreview.toLowerCase().includes(q)) return true;
          for (const v of Object.values(t.request.headers)) {
            if (v.toLowerCase().includes(q)) return true;
          }
          if (t.response) {
            for (const v of Object.values(t.response.headers)) {
              if (v.toLowerCase().includes(q)) return true;
            }
          }
          return false;
        });
      }
    
      clearTraffic(): number {
        const count = this.traffic.length;
        this.traffic.length = 0;
        this.pendingRequests.clear();
        this.pendingRawBodies.clear();
        return count;
      }
    
      // ── Persistent Sessions ──
    
      isSessionPersistenceEnabled(): boolean {
        return this.sessionStore.isActive();
      }
    
      getSessionCaptureProfile(): "preview" | "full" | null {
        return this.sessionStore.getActiveProfile();
      }
    
      async startSession(options: SessionStartOptions = {}): Promise<SessionManifest> {
        return await this.sessionStore.startSession(options);
      }
    
      async stopSession(): Promise<SessionManifest | null> {
        return await this.sessionStore.stopSession();
      }
    
      getSessionStatus(): object {
        return this.sessionStore.getRuntimeStatus();
      }
    
      async listSessions(): Promise<Array<SessionManifest & { diskUsageMb: number }>> {
        return await this.sessionStore.listSessions();
      }
    
      async getSession(sessionId: string): Promise<SessionManifest> {
        return await this.sessionStore.getSession(sessionId);
      }
    
      async querySession(sessionId: string, query: SessionQuery): Promise<SessionQueryResult> {
        return await this.sessionStore.querySession(sessionId, query);
      }
    
      async getSessionExchange(
        sessionId: string,
        opts: { seq?: number; exchangeId?: string; includeBody?: boolean },
      ): Promise<{ index: SessionIndexEntry; record?: unknown }> {
        return await this.sessionStore.getSessionExchange(sessionId, opts);
      }
    
      async exportSessionHar(
  • The tool handler function for "proxy_export_har" which processes the input parameters and calls proxyManager.exportSessionHar.
    async ({ session_id, output_file, include_bodies, method, hostname_contains, url_contains, status_code, from_ts, to_ts, text, sort }) => {
      try {
        const exported = await proxyManager.exportSessionHar(session_id, {
          outputFile: output_file,
          includeBodies: include_bodies,
          query: {
            method,
            hostnameContains: hostname_contains,
            urlContains: url_contains,
            statusCode: status_code,
            fromTs: from_ts,
            toTs: to_ts,
            text,
            sort,
          },
        });
  • Registration of the "proxy_export_har" MCP tool with its input schema definition.
    server.tool(
      "proxy_export_har",
      "Export a recorded session (or filtered subset) to HAR format.",
      {
        session_id: z.string().describe("Session ID"),
        output_file: z.string().optional().describe("Output HAR file path"),
        include_bodies: z.boolean().optional().default(true).describe("Include body text when available"),
        method: z.string().optional(),
        hostname_contains: z.string().optional(),
        url_contains: z.string().optional(),
        status_code: z.number().optional(),
        from_ts: z.number().optional(),
        to_ts: z.number().optional(),
        text: z.string().optional(),
        sort: z.enum(["asc", "desc"]).optional().default("asc"),
      },

Tool Definition Quality

Score is being calculated. Check back soon.

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/yfe404/proxy-mcp'

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