Skip to main content
Glama

proxy_mobile_teardown

Deactivates Android proxy target, stops transparent and explicit listeners, and generates iptables removal script to restore NetworkManager management.

Instructions

Reverse proxy_mobile_setup: deactivate the Android target if any, stop the transparent + explicit listeners, and emit a sudo-runnable script that removes the iptables rules and restores NetworkManager management.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
ap_ifaceNoAP/USB interface name. Auto-detected via cdc_ncm if omitted.
ap_subnetNoSubnet the AP serves (must match setup).192.168.4.0/24
egress_ifaceNoHost's egress iface (must match setup).
block_quicNoWhether the QUIC DROP rule was set up (so we know to remove it).
android_target_idNoAndroid target ID to deactivate (from proxy_mobile_setup response).
stop_proxyNoAlso stop the explicit proxy (default: keep it running for continued use).

Implementation Reference

  • The handler for proxy_mobile_teardown: deactivates Android target, stops transparent/explicit listeners, emits a teardown sudo script that removes iptables rules and restores NetworkManager.
    server.tool(
      "proxy_mobile_teardown",
      "Reverse proxy_mobile_setup: deactivate the Android target if any, stop the transparent + explicit listeners, and emit a sudo-runnable script that removes the iptables rules and restores NetworkManager management.",
      {
        ap_iface: z.string().optional().describe("AP/USB interface name. Auto-detected via cdc_ncm if omitted."),
        ap_subnet: z.string().optional().default("192.168.4.0/24").describe("Subnet the AP serves (must match setup)."),
        egress_iface: z.string().optional().describe("Host's egress iface (must match setup)."),
        block_quic: z.boolean().optional().default(true).describe("Whether the QUIC DROP rule was set up (so we know to remove it)."),
        android_target_id: z.string().optional().describe("Android target ID to deactivate (from proxy_mobile_setup response)."),
        stop_proxy: z.boolean().optional().default(false).describe("Also stop the explicit proxy (default: keep it running for continued use)."),
      },
      async ({ ap_iface, ap_subnet, egress_iface, block_quic, android_target_id, stop_proxy }) => {
        try {
          let apIface = ap_iface;
          if (!apIface) {
            const detected = detectApIface();
            if (!detected) {
              return { content: [{ type: "text", text: JSON.stringify({
                status: "error",
                error: "Could not auto-detect AP iface for teardown. Pass ap_iface explicitly.",
              }) }] };
            }
            apIface = detected.iface;
          }
          const egressIface = egress_iface ?? detectEgressIface();
    
          if (android_target_id) {
            await interceptorManager.deactivate("android-adb", android_target_id).catch(() => {});
          }
          if (proxyManager.isTransparentRunning()) {
            await proxyManager.stopTransparent().catch(() => {});
          }
          if (stop_proxy && proxyManager.isRunning()) {
            await proxyManager.stop().catch(() => {});
          }
    
          const scriptPath = writeScript(
            buildTeardownScript({
              apIface,
              apSubnet: ap_subnet,
              egressIface,
              blockQuic: block_quic,
            }),
            "mobile-teardown",
          );
    
          return {
            content: [{
              type: "text",
              text: JSON.stringify({
                status: "success",
                ap_iface: apIface,
                egress_iface: egressIface,
                android_target_deactivated: !!android_target_id,
                transparent_stopped: true,
                explicit_stopped: stop_proxy,
                sudo_script: scriptPath,
                sudo_command: `sudo bash ${scriptPath}`,
              }),
            }],
          };
        } catch (e) {
          return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: errorToString(e) }) }] };
        }
      },
  • Zod schema defining the input parameters for proxy_mobile_teardown.
    {
      ap_iface: z.string().optional().describe("AP/USB interface name. Auto-detected via cdc_ncm if omitted."),
      ap_subnet: z.string().optional().default("192.168.4.0/24").describe("Subnet the AP serves (must match setup)."),
      egress_iface: z.string().optional().describe("Host's egress iface (must match setup)."),
      block_quic: z.boolean().optional().default(true).describe("Whether the QUIC DROP rule was set up (so we know to remove it)."),
      android_target_id: z.string().optional().describe("Android target ID to deactivate (from proxy_mobile_setup response)."),
      stop_proxy: z.boolean().optional().default(false).describe("Also stop the explicit proxy (default: keep it running for continued use)."),
  • src/index.ts:73-73 (registration)
    Registration of all mobile tools (including proxy_mobile_teardown) on the MCP server.
    registerMobileTools(server);
  • Registration function that calls server.tool() to register proxy_mobile_teardown (and setup/detect_iface) on the MCP server.
    export function registerMobileTools(server: McpServer): void {
      server.tool(
        "proxy_mobile_setup",
        "One-command mobile capture: start explicit + transparent listeners, optionally inject the CA on an Android device, and emit a sudo-runnable script that wires iptables/sysctl/nmcli on the AP iface. Designed to pair with the proxy-ap-card firmware (ESP32-S3 rogue AP over USB-NCM).",
        {
          ap_iface: z.string().optional().describe("AP/USB interface name. Auto-detected via cdc_ncm driver if omitted."),
          ap_address: z.string().optional().default("192.168.99.2/24").describe("Laptop-side address on the AP iface (default: 192.168.99.2/24, matches proxy-ap-card firmware)."),
          ap_subnet: z.string().optional().default("192.168.4.0/24").describe("Subnet the AP serves to clients (default: 192.168.4.0/24, matches proxy-ap-card firmware)."),
          egress_iface: z.string().optional().describe("Host's internet-facing iface. Auto-detected from /proc/net/route if omitted."),
          explicit_port: z.number().optional().default(8080).describe("Port for the explicit HTTP proxy (default: 8080)."),
          transparent_port: z.number().optional().default(8443).describe("Port for the transparent HTTPS listener (default: 8443)."),
          block_quic: z.boolean().optional().default(true).describe("Drop UDP/443 on the AP iface so apps fall back to TCP/TLS (capturable). Default: true."),
          upstream_proxy_url: z.string().optional().describe("Optional upstream proxy URL (socks5://user:pass@host:port or http://...). Sets the global upstream for BOTH listeners."),
          android_serial: z.string().optional().describe("ADB serial of an Android device to inject the CA on. If omitted, no cert injection is attempted."),
          inject_cert: z.boolean().optional().default(true).describe("Inject the CA into the Android device's system store. Ignored if android_serial is omitted."),
        },
        async ({
          ap_iface,
          ap_address,
          ap_subnet,
          egress_iface,
          explicit_port,
          transparent_port,
          block_quic,
          upstream_proxy_url,
          android_serial,
          inject_cert,
        }) => {
          try {
            // 1. Resolve AP iface.
            let apIface = ap_iface;
            let ifaceReason = "user-provided";
            if (!apIface) {
              const detected = detectApIface();
              if (!detected) {
                return { content: [{ type: "text", text: JSON.stringify({
                  status: "error",
                  error: "Could not auto-detect an AP interface. Plug in the proxy-ap-card (or another cdc_ncm USB NCM device) and retry, or pass ap_iface explicitly. Known interfaces:",
                  interfaces: readdirSync("/sys/class/net").filter((n) => n !== "lo"),
                }) }] };
              }
              apIface = detected.iface;
              ifaceReason = detected.reason;
            }
    
            // 2. Resolve egress.
            const egressIface = egress_iface ?? detectEgressIface();
    
            // 3. Explicit proxy.
            let explicitPortUsed = explicit_port;
            if (!proxyManager.isRunning()) {
              const started = await proxyManager.start(explicit_port);
              explicitPortUsed = started.port;
            } else {
              explicitPortUsed = proxyManager.getPort() ?? explicit_port;
            }
    
            // 4. Transparent listener.
            let transparentPortUsed = transparent_port;
            if (!proxyManager.isTransparentRunning()) {
              const startedTp = await proxyManager.startTransparent(transparent_port);
              transparentPortUsed = startedTp.port;
            } else {
              transparentPortUsed = proxyManager.getTransparentPort() ?? transparent_port;
            }
    
            // 5. Upstream (optional).
            let upstreamSet = false;
            if (upstream_proxy_url) {
              await proxyManager.setGlobalUpstream({ proxyUrl: upstream_proxy_url });
              upstreamSet = true;
            }
    
            // 6. Android CA injection (optional).
            let certInjected = false;
            let androidTargetId: string | null = null;
            if (android_serial && inject_cert) {
              const cert = proxyManager.getCert();
              if (!cert) throw new Error("Proxy started but no cert was generated — this is a bug.");
              const result = await interceptorManager.activate("android-adb", {
                proxyPort: explicitPortUsed,
                certPem: cert.cert,
                certFingerprint: cert.fingerprint,
                serial: android_serial,
                injectCert: true,
                setupTunnel: false,  // AP + iptables handle routing; no reverse tunnel needed
                setWifiProxy: false, // same reason
              });
              certInjected = true;
              androidTargetId = result.targetId;
            }
    
            // 7. Emit sudo script.
            const scriptPath = writeScript(
              buildSetupScript({
                apIface,
                apAddress: ap_address,
                apSubnet: ap_subnet,
                egressIface,
                explicitPort: explicitPortUsed,
                transparentPort: transparentPortUsed,
                blockQuic: block_quic,
              }),
              "mobile-setup",
            );
    
            return {
              content: [{
                type: "text",
                text: JSON.stringify({
                  status: "success",
                  ap_iface: apIface,
                  ap_iface_reason: ifaceReason,
                  ap_address,
                  ap_subnet,
                  egress_iface: egressIface,
                  explicit_port: explicitPortUsed,
                  transparent_port: transparentPortUsed,
                  block_quic,
                  upstream_set: upstreamSet,
                  cert_injected: certInjected,
                  android_target_id: androidTargetId,
                  sudo_script: scriptPath,
                  sudo_command: `sudo bash ${scriptPath}`,
                  next_steps: [
                    `Run:  sudo bash ${scriptPath}`,
                    "Connect the target device to the proxy-ap WiFi network (credentials live in the proxy-ap-card firmware).",
                    "Call proxy_list_traffic — transparent HTTPS exchanges appear with source=\"transparent\", HTTP with source=\"explicit\".",
                    "When done, call proxy_mobile_teardown and run the emitted sudo script.",
                  ],
                }),
              }],
            };
          } catch (e) {
            return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: errorToString(e) }) }] };
          }
        },
      );
    
      server.tool(
        "proxy_mobile_teardown",
        "Reverse proxy_mobile_setup: deactivate the Android target if any, stop the transparent + explicit listeners, and emit a sudo-runnable script that removes the iptables rules and restores NetworkManager management.",
        {
          ap_iface: z.string().optional().describe("AP/USB interface name. Auto-detected via cdc_ncm if omitted."),
          ap_subnet: z.string().optional().default("192.168.4.0/24").describe("Subnet the AP serves (must match setup)."),
          egress_iface: z.string().optional().describe("Host's egress iface (must match setup)."),
          block_quic: z.boolean().optional().default(true).describe("Whether the QUIC DROP rule was set up (so we know to remove it)."),
          android_target_id: z.string().optional().describe("Android target ID to deactivate (from proxy_mobile_setup response)."),
          stop_proxy: z.boolean().optional().default(false).describe("Also stop the explicit proxy (default: keep it running for continued use)."),
        },
        async ({ ap_iface, ap_subnet, egress_iface, block_quic, android_target_id, stop_proxy }) => {
          try {
            let apIface = ap_iface;
            if (!apIface) {
              const detected = detectApIface();
              if (!detected) {
                return { content: [{ type: "text", text: JSON.stringify({
                  status: "error",
                  error: "Could not auto-detect AP iface for teardown. Pass ap_iface explicitly.",
                }) }] };
              }
              apIface = detected.iface;
            }
            const egressIface = egress_iface ?? detectEgressIface();
    
            if (android_target_id) {
              await interceptorManager.deactivate("android-adb", android_target_id).catch(() => {});
            }
            if (proxyManager.isTransparentRunning()) {
              await proxyManager.stopTransparent().catch(() => {});
            }
            if (stop_proxy && proxyManager.isRunning()) {
              await proxyManager.stop().catch(() => {});
            }
    
            const scriptPath = writeScript(
              buildTeardownScript({
                apIface,
                apSubnet: ap_subnet,
                egressIface,
                blockQuic: block_quic,
              }),
              "mobile-teardown",
            );
    
            return {
              content: [{
                type: "text",
                text: JSON.stringify({
                  status: "success",
                  ap_iface: apIface,
                  egress_iface: egressIface,
                  android_target_deactivated: !!android_target_id,
                  transparent_stopped: true,
                  explicit_stopped: stop_proxy,
                  sudo_script: scriptPath,
                  sudo_command: `sudo bash ${scriptPath}`,
                }),
              }],
            };
          } catch (e) {
            return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: errorToString(e) }) }] };
          }
        },
      );
    
      server.tool(
        "proxy_mobile_detect_iface",
        "Auto-detect the USB-NCM interface the proxy-ap-card presents as (via the cdc_ncm driver). Returns null + iface list if none found.",
        {},
        async () => {
          try {
            const detected = detectApIface();
            if (!detected) {
              return {
                content: [{
                  type: "text",
                  text: JSON.stringify({
                    status: "success",
                    found: false,
                    interfaces: readdirSync("/sys/class/net").filter((n) => n !== "lo"),
                  }),
                }],
              };
            }
            return {
              content: [{
                type: "text",
                text: JSON.stringify({
                  status: "success",
                  found: true,
                  iface: detected.iface,
                  reason: detected.reason,
                }),
              }],
            };
          } catch (e) {
            return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: errorToString(e) }) }] };
          }
        },
      );
    }
  • Helper that builds the sudo bash teardown script content to remove iptables rules, disable IP forwarding, flush the AP address, and re-enable NetworkManager management.
    function buildTeardownScript(opts: {
      apIface: string;
      apSubnet: string;
      egressIface: string;
      blockQuic: boolean;
    }): string {
      return `#!/bin/bash
    # proxy-mcp mobile capture — remove host-side network rules.
    set -u
    
    AP_IFACE="${opts.apIface}"
    AP_SUBNET="${opts.apSubnet}"
    EGRESS_IFACE="${opts.egressIface}"
    
    iptables -t nat -D PREROUTING -i "\$AP_IFACE" -j PROXY_MCP_PREROUTING 2>/dev/null || true
    iptables -t nat -F PROXY_MCP_PREROUTING 2>/dev/null || true
    iptables -t nat -X PROXY_MCP_PREROUTING 2>/dev/null || true
    ${opts.blockQuic
      ? `iptables -D FORWARD -i "\$AP_IFACE" -p udp --dport 443 -j DROP 2>/dev/null || true\n`
      : ""}iptables -t nat -D POSTROUTING -s "\$AP_SUBNET" -o "\$EGRESS_IFACE" -j MASQUERADE 2>/dev/null || true
    
    sysctl -w net.ipv4.ip_forward=0 >/dev/null
    
    ip addr flush dev "\$AP_IFACE" 2>/dev/null || true
    nmcli device set "\$AP_IFACE" managed yes 2>/dev/null || true
    
    echo "Teardown complete."
    `;
    }
Behavior4/5

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

The description discloses key side effects: deactivating Android target, stopping listeners, and emitting a sudo-runnable script that modifies iptables and NetworkManager. Without annotations, this provides good transparency, though it is unclear whether the script is auto-executed or just emitted.

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?

A single sentence that is front-loaded with the core purpose ('Reverse proxy_mobile_setup') and then enumerates actions concisely. No redundant information.

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 no annotations and no output schema, the description covers the main actions but omits details about return value and idempotency. It is fairly complete for a teardown tool with 6 parameters.

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 coverage is 100%, so baseline is 3. The description adds minimal extra meaning over the schema; it only indirectly relates to android_target_id and stop_proxy. No new semantic details for ap_iface, ap_subnet, egress_iface, or block_quic.

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 explicitly states 'Reverse proxy_mobile_setup', clearly identifying it as the counterpart to the setup tool. It lists specific actions (deactivate Android target, stop listeners, emit script) that distinguish it from sibling tools like proxy_mobile_setup.

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

Usage Guidelines4/5

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

The phrase 'Reverse proxy_mobile_setup' strongly implies it should be used after setup. However, it does not explicitly state prerequisites or when not to use it, leaving some ambiguity about preconditions.

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

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