paperclipai/paperclip · error · ToolGatewayHttpError

mcp_transport_unsupported

mcp_transport_unsupported

Error message

Assigned MCP connection transport is unsupported

What it means

This ToolGatewayHttpError (HTTP 501) is thrown by the tool-call dispatch when an assigned MCP connection has a transport that is neither 'remote' (handled by callRemoteConnectionProtocol) nor 'local_stdio'. Only those two transports are implemented, so anything else is rejected as unsupported. It signals an unimplemented/unknown transport value in the connection record, not a transient failure.

Solutions

  1. Fix the connection record so transport is a supported value ('remote' or 'local_stdio'): UPDATE connections SET transport = 'remote' WHERE id = ... (matching how the connection actually connects).
  2. Re-create the connection through the UI/install flow so the transport is set to a supported value by the application instead of hand-editing rows.
  3. If the source system genuinely uses another transport (e.g. SSE), find or build a supported equivalent — remote protocol transport — rather than keeping the unsupported value.
  4. Check for schema/validation drift: add a validator on connection creation so unsupported transport values are rejected at write time.

Example fix

// before: unknown transport in the connection row
UPDATE connections SET transport = 'sse' WHERE id = $1; -- gateway throws 501
// after: use a supported transport
UPDATE connections SET transport = 'remote' WHERE id = $1;
Defensive patterns

Strategy: type-guard

Validate before calling

const SUPPORTED_TRANSPORTS = ["remote", "local_stdio"] as const;
if (!SUPPORTED_TRANSPORTS.includes(connection.transport)) {
  throw new Error(`Connection ${connection.id} has unsupported transport '${connection.transport}'.`);
}

Type guard

type SupportedTransport = "remote" | "local_stdio";
function isSupportedTransport(t: string): t is SupportedTransport {
  return t === "remote" || t === "local_stdio";
}

Try / catch

try {
  await callAssignedMcpTool(session, connectionId, params);
} catch (e) {
  if (e instanceof ToolGatewayHttpError && e.code === "mcp_transport_unsupported") {
    return { status: "unsupported_transport", remediation: "Re-create the connection with transport 'remote' or 'local_stdio'." };
  }
  throw e;
}

Prevention

When it happens

Trigger: A tool call is dispatched to a connection whose transport column holds a value other than 'remote' or 'local_stdio' — e.g. a hand-inserted or migrated connection row with transport 'http', 'sse', 'websocket', or an empty/legacy value — reaching the check at tool-gateway.ts:5206.

Common situations: Direct database edits or migrations introducing new transport kinds before gateway support landed; importing connection configs from another tool that uses different transport names; a partially rolled-out new transport feature where rows exist but the dispatch branch isn't implemented yet.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/c2712e918d5b6183. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/tool-gateway.ts:5206

    );
    return record.result;
  }

  async function callAssignedConnectionProtocol(input: {
    session: ToolGatewaySession;
    connection: typeof toolConnections.$inferSelect;
    method: string;
    params?: Record<string, unknown>;
    callerHeaders?: Record<string, string | string[] | undefined>;
  }): Promise<unknown> {
    if (input.connection.transport === "mcp_remote") {
      return callRemoteConnectionProtocol({
        ...input,
        params: input.params ?? {},
      });
    }
    if (input.connection.transport !== "local_stdio") {
      throw new ToolGatewayHttpError(
        501,
        "Assigned MCP connection transport is unsupported",
        "mcp_transport_unsupported",
      );
    }
    const template = await resolveLocalStdioRuntimeTemplate(input.connection);
    const grant = await resolveConnectionGrant(input.session, input.connection);
    const env = await localStdioEnvironment(
      input.session,
      input.connection,
      template,
      grant,
    );
    return runtimeSupervisor.useConnectionSlot(
      {
        companyId: input.session.companyId,
        applicationId: input.connection.applicationId,
        connectionId: input.connection.id,

View on GitHub (pinned to 3f1d897a7c)