All posts

Debugging Claude Connectors: Fix Common Errors in Development and Production (May 2026)

Abe Wheeler
Claude ConnectorsClaude Connector TestingClaude Connector FrameworkClaude AppsMCP AppsMCP App FrameworkMCP App TestingChatGPT Apps
Debug Claude Connectors locally with the sunpeak inspector before connecting to Claude.

Debug Claude Connectors locally with the sunpeak inspector before connecting to Claude.

TL;DR: Debug Claude Connectors locally with the sunpeak MCP App Inspector before you debug them in Claude. Most issues fall into eight buckets: reachability, transport, OAuth discovery, tool selection, UI resource rendering, output size, timeouts, and real-host differences. This updated May 2026 guide walks through each one with concrete checks.

You built a Claude Connector. The MCP server runs. A local test passes. Then you add it to Claude and something breaks. Claude may say it cannot reach the server, ignore the tool, fail OAuth, or render a blank MCP App card.

This post is a practical debugging flow for Claude Connectors. It also helps if you are building portable MCP Apps that run in Claude and ChatGPT, because many failures come from the same MCP primitives: tools, resources, Streamable HTTP, OAuth metadata, and sandboxed UI.

Start With a Local Repro

Do not start by clicking through Claude. Start with a deterministic local repro.

For a sunpeak project:

npx sunpeak new sunpeak-app
cd sunpeak-app
pnpm dev

Open http://localhost:3000, select Claude from the Host dropdown, and load the tool or simulation that fails. The inspector replicates the Claude and ChatGPT MCP App runtimes locally, including host context, theme, display mode, tool input, tool result, and iframe rendering.

For an existing MCP server:

npx sunpeak inspect --server http://localhost:8000/mcp

You can also inspect a stdio server without changing the project:

npx sunpeak inspect --server "python server.py"

This matters because a real Claude session mixes several systems at once: public networking, OAuth, model tool choice, tool execution, resource loading, iframe sandboxing, and user permissions. The local inspector lets you isolate the tool and UI first. If the local Claude host fails, fix code. If local passes and real Claude fails, debug connection, auth, or production-host behavior.

Connection Failures

Symptom: Claude says it could not reach the MCP server, the connector appears disconnected, or requests never hit your server logs.

First, verify the MCP endpoint from a public network:

curl -i https://your-server.example.com/mcp

A 401, 405, or JSON-RPC error can be fine during this check because it proves the endpoint is reachable. A timeout, DNS failure, 502, WAF block, or redirect to another host is the problem.

Check the URL

Claude needs the actual MCP endpoint. If your server listens at /mcp, register that exact path:

# Correct
https://abc123.ngrok-free.app/mcp

# Wrong: missing MCP path
https://abc123.ngrok-free.app

# Wrong: points to your app route, not the MCP endpoint
https://abc123.ngrok-free.app/dashboard

The path matters for OAuth too. If the MCP endpoint is https://your-server.example.com/mcp, protected resource metadata may need to live at:

https://your-server.example.com/.well-known/oauth-protected-resource/mcp

That detail catches a lot of custom connector builds because the root well-known URL may work while the path-specific metadata is missing.

Check the Tunnel

If you use ngrok during development:

ngrok http 8000

Copy the HTTPS forwarding URL and add /mcp. If ngrok’s local inspector at http://localhost:4040 shows 502 Bad Gateway, your MCP server is not running on the port you tunneled. If ngrok shows no request when Claude connects, Claude is not using the URL you think it is, or the request is blocked before it reaches the tunnel.

Check Transport

Anthropic’s connector docs say Claude supports Streamable HTTP and the legacy HTTP+SSE transport, with legacy HTTP+SSE being deprecated. New work should target Streamable HTTP.

If your server only supports stdio, Claude cannot connect to it by URL. Use a remote MCP server, a hosted bridge, or a framework server that exposes Streamable HTTP. sunpeak projects expose a production MCP server on port 8000 after build and start, and the local inspector can still test stdio servers separately.

Check Required Headers

For Streamable HTTP, MCP clients send JSON-RPC over HTTP and include protocol headers after initialization. If you write custom middleware, do not strip headers such as:

Accept: application/json, text/event-stream
MCP-Protocol-Version: 2025-06-18
Mcp-Session-Id: ...

You do not need to hand-code these in sunpeak, but you should check proxies, serverless adapters, and API gateways. They can drop unknown headers, coerce streaming responses, or redirect POST /mcp to a trailing-slash URL.

Claude Does Not Call Your Tools

Symptom: You ask Claude to use the connector, but it answers in text or uses the wrong tool.

This is usually a tool design issue. Claude chooses tools from the tool name, description, input schema, and the user’s current conversation. If multiple tools look similar, or one tool promises too much, the model may avoid it.

Compare these descriptions:

// Bad: too vague for reliable tool selection
export const tool: AppToolConfig = {
  title: 'Query Service',
  description: 'Interact with the service',
};

// Better: clear trigger, object, filters, and result shape
export const tool: AppToolConfig = {
  title: 'Search Tickets',
  description:
    'Search support tickets by keyword, status, priority, or assignee. Returns ticket ID, title, status, priority, updated date, and assignee.',
};

Good tool descriptions answer four questions:

  1. What user request should trigger this tool?
  2. What system or object does it query or change?
  3. Which inputs does it accept?
  4. What shape does it return?

For deeper tool design patterns, see Designing Claude Connector Tools.

Also check these non-code causes:

  • The connector is installed but not enabled for the conversation.
  • Too many connectors or tools are enabled, which makes tool choice noisier.
  • The tool name and description say different things.
  • The schema allows broad optional input, so Claude cannot infer what is required.
  • The user prompt does not mention the data source, so Claude has no reason to call the connector.

When debugging, disable unrelated connectors, ask a direct prompt like “Use the Acme Support connector to search open tickets for billing”, and inspect whether the tool input matches your schema.

UI Not Rendering

Symptom: Claude calls your tool, but the result appears as plain text, a blank card, or a broken iframe instead of an interactive MCP App.

MCP Apps work by linking a tool to a UI resource. The host can preload the ui:// resource, run it in a sandboxed iframe, and pass the tool result to the app. In a sunpeak app, that means your tool config points to a resource and your handler returns structuredContent that the resource can render.

Return Structured Content

If you only return text, Claude has text to show. It does not have structured data for your UI.

// Renders the resource UI
export default async function (args: Args) {
  return {
    structuredContent: {
      title: args.title,
      items: await fetchItems(args.query),
    },
  };
}

// Shows text only
export default async function (args: Args) {
  const items = await fetchItems(args.query);

  return {
    content: [{ type: 'text', text: JSON.stringify(items) }],
  };
}

Match the Tool to the Resource

In sunpeak, the tool’s resource field must match the resource file name:

// src/tools/search-tickets.ts
export const tool: AppToolConfig = {
  resource: 'ticket-list', // matches src/resources/ticket-list.tsx
  title: 'Search Tickets',
  description: 'Search support tickets and render matching tickets as cards.',
};

If you use a lower-level MCP server, check the tool metadata points to the right ui:// resource and the resource returns HTML with the MCP App MIME type.

Build the Production Bundle

Claude does not load your Vite dev server as a script source. It renders a bundled app resource in a sandbox. Run the production build before blaming Claude:

pnpm build

Check for TypeScript errors, missing imports, browser-incompatible packages, dynamic imports that your bundler cannot include, and CSP assumptions. A resource can work under HMR and still fail in a production iframe if it depends on a dev-only module.

Check Browser DevTools

Open DevTools in Claude and select the iframe context for your resource. Client-side exceptions, CSP violations, missing assets, and blocked network calls usually appear there. In the sunpeak inspector, use the same habit: server logs explain tool failures, browser logs explain resource failures.

For resource state handling, use the MCP App error handling guide.

OAuth Errors

Symptom: Users cannot connect the connector, the OAuth redirect fails, or Claude shows “Authorization with the MCP server failed.”

OAuth failures are easier to debug if you separate three stages:

  1. Claude discovers your protected resource metadata.
  2. Claude discovers your authorization server metadata.
  3. Claude completes authorization code with PKCE and calls your MCP server with the token.

Use the Current Callback URL

For hosted Claude surfaces, Anthropic’s current connector docs list:

https://claude.ai/api/mcp/auth_callback

Older examples and blog posts may mention oauth_callback or include both claude.ai and claude.com. Check your provider configuration against the current Anthropic authentication docs before you chase app code.

Claude Code uses a loopback redirect flow, so do not assume one callback covers every surface.

Verify Discovery Metadata

From a public network, run:

curl -i https://your-server.example.com/.well-known/oauth-protected-resource
curl -i https://your-server.example.com/.well-known/oauth-authorization-server
curl -i https://your-server.example.com/.well-known/openid-configuration

If your MCP server URL has a path, test the path-specific protected resource metadata too:

curl -i https://your-server.example.com/.well-known/oauth-protected-resource/mcp

The protected resource metadata should identify your MCP server and authorization servers. The authorization server metadata should advertise Dynamic Client Registration, Client ID Metadata Document support, or match the pre-registered credentials you gave Claude.

Check Audience, Issuer, and PKCE

Common auth mistakes:

  • Your token issuer does not match the authorization server metadata.
  • Your MCP server validates the wrong aud value.
  • Your auth server does not support PKCE S256.
  • A CDN or WAF blocks Anthropic’s discovery or token requests.
  • Your server redirects the MCP endpoint to a different host.
  • You return HTML errors instead of JSON metadata from well-known routes.

Anthropic’s official troubleshooting checklist is worth running before you file a bug because it maps the visible Claude errors to root causes.

Output Size Errors

Symptom: The tool runs, but Claude shows an incomplete answer, drops data, or loses details that your server returned.

Do not design tools around the maximum. Anthropic currently documents about 150,000 characters for Claude.ai and Claude Desktop tool results, while Claude Code defaults to 25,000 tokens via MAX_MCP_OUTPUT_TOKENS. Those limits can still be too large for a useful conversation, and huge payloads make tool choice, follow-up reasoning, and UI rendering worse.

Return the smallest useful shape:

export default async function (args: { query: string; cursor?: string }) {
  const { items, nextCursor } = await searchTickets(args.query, {
    cursor: args.cursor,
    limit: 20,
  });

  return {
    structuredContent: {
      items: items.map((ticket) => ({
        id: ticket.id,
        title: ticket.title,
        status: ticket.status,
        priority: ticket.priority,
        updatedAt: ticket.updatedAt,
      })),
      nextCursor,
      hasMore: Boolean(nextCursor),
    },
  };
}

Then add a second get-ticket-details tool for comments, attachments, audit history, and full descriptions. This keeps search fast and gives Claude a clear next action when the user asks for detail.

Timeout Errors

Symptom: Claude starts the tool call, then the spinner runs until the host reports an error.

Claude.ai and Claude Desktop document a 300 second timeout. Your target should be much lower. Long-running tools make the conversation feel stuck and can fail through proxies, upstream APIs, serverless limits, or browser networking before Claude’s outer timeout.

Add explicit timeouts around outbound calls:

export default async function (args: Args) {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 30_000);

  try {
    const response = await fetch('https://api.example.com/data', {
      signal: controller.signal,
    });

    if (!response.ok) {
      throw new Error(`API returned ${response.status}`);
    }

    return { structuredContent: await response.json() };
  } finally {
    clearTimeout(timeout);
  }
}

For work that may take minutes, use a two-tool pattern:

  1. start-report validates input, creates a job, and returns { jobId, status: 'queued' }.
  2. get-report-status returns progress and, when ready, the result summary or a link to the generated artifact.

Claude can call the status tool in a follow-up, and your server does not need to hold one request open.

Blank Screen in Claude

Symptom: The tool call succeeds and Claude reserves space for the UI, but the card is blank.

Most blank screens are frontend exceptions. The resource gets data you did not expect, reads a missing property, or assumes browser APIs that the iframe sandbox does not provide.

Handle every resource state:

import { SafeArea, useToolData } from 'sunpeak';

interface TicketData {
  title: string;
}

export function TicketResource() {
  const { output, isLoading, isError, isCancelled, cancelReason } =
    useToolData<unknown, TicketData>(undefined, undefined);

  if (isLoading) return <SafeArea className="p-4">Loading...</SafeArea>;
  if (isCancelled) return <SafeArea className="p-4">{cancelReason ?? 'Cancelled'}</SafeArea>;
  if (isError) return <SafeArea className="p-4">Something went wrong.</SafeArea>;
  if (!output) return <SafeArea className="p-4">No ticket returned.</SafeArea>;

  return (
    <SafeArea className="p-4">
      <h1>{output.title}</h1>
    </SafeArea>
  );
}

Then test the blank-screen cases directly:

  • structuredContent is missing.
  • structuredContent is {}.
  • The list is empty.
  • The server returns isError.
  • The user cancels the tool call.
  • The host is in dark mode or a narrow viewport.

sunpeak simulations are useful here because you can create one fixture for each state and run the same resource through Claude, ChatGPT, mobile, desktop, light, and dark modes.

CSP and External API Calls

Symptom: The UI renders, but images, fonts, scripts, maps, fetch calls, or nested iframes fail.

MCP Apps run in sandboxed iframes, and hosts enforce Content Security Policy based on declared resource metadata. If your resource needs to call an external API or load assets from a CDN, declare those domains. Do not assume the iframe can reach everything your normal website can reach.

For connector debugging, ask:

  • Does this data fetch belong in the tool handler instead of the UI?
  • Did I declare connectDomains for fetch or WebSocket calls?
  • Did I declare resourceDomains for scripts, images, styles, fonts, or media?
  • Did I accidentally depend on cookies or local storage from the parent site?
  • Does the UI need a permission such as clipboard, geolocation, camera, or microphone?

As a rule, fetch private business data in the server-side tool, return structuredContent, and let the UI render it. Use client-side fetch only for public assets, same-session UI updates, or cases where the MCP App spec and host CSP clearly allow it.

Useful Debug Commands

Use this quick pass before you debug in the real host:

# Start a sunpeak project with inspector and local MCP server
pnpm dev

# Inspect any existing MCP server
npx sunpeak inspect --server http://localhost:8000/mcp

# Build the production resource and tool bundles
pnpm build

# Run unit and E2E tests
pnpm test

# Run visual regression tests
pnpm test:visual

# Run live host checks when real-host behavior matters
pnpm test:live

# Tunnel local port 8000 for a real Claude connection
ngrok http 8000

# Check public reachability
curl -i https://your-public-url.example.com/mcp

# Check OAuth protected resource metadata
curl -i https://your-public-url.example.com/.well-known/oauth-protected-resource/mcp

For a broader automated testing plan, see the complete guide to testing MCP Apps, E2E testing for MCP Apps, and CI/CD setup for MCP Apps.

When to Debug in Real Claude

Use the sunpeak inspector for the normal loop. Switch to real Claude when the bug depends on Claude infrastructure:

  • Public connector registration and org permissions.
  • OAuth redirects, token exchange, and Anthropic egress.
  • Directory review behavior.
  • Hosted iframe CSP differences.
  • Real model tool selection across multiple enabled connectors.
  • Claude-specific timeout and output-limit behavior.

For those cases, keep the repro narrow. Connect one tunnel, enable one connector, call one tool, and capture server logs, browser console logs, and the exact prompt. If a failure only happens in the real host, turn it into a live test so you do not have to repeat the same manual checks on every release.

The Short Version

Most Claude Connector bugs are not mysterious. They are usually one of these:

  1. Claude cannot reach the MCP endpoint.
  2. The server does not support the transport Claude is using.
  3. OAuth metadata, callback, issuer, audience, or PKCE is wrong.
  4. Claude cannot infer the right tool from the description and schema.
  5. The tool returns text when the UI needs structuredContent.
  6. The resource crashes inside the iframe.
  7. The output is too large.
  8. The tool takes too long.

Start in the sunpeak MCP App Inspector, reproduce the failing state locally, then use real Claude only for the parts that require Claude’s hosted environment. That keeps debugging fast and gives you tests you can keep.

Get Started

Documentation →
npx sunpeak new

Further Reading

Frequently Asked Questions

Why is my Claude Connector not working?

The most common causes are: the MCP server is not publicly reachable, the connector URL points to the wrong MCP path, the server only supports stdio, OAuth discovery metadata is missing or invalid, Claude cannot pick the right tool from the tool description, or the MCP App resource throws inside the iframe. Start with server logs and a public curl against your MCP endpoint, then test the same tool in the sunpeak inspector.

How do I debug a Claude Connector locally?

Use sunpeak to run a local MCP App inspector with "pnpm dev" for a sunpeak project or "npx sunpeak inspect --server http://localhost:8000/mcp" for an existing MCP server. Select Claude from the Host dropdown to test tools, resources, theme, display mode, structuredContent, and iframe behavior before you connect the server to Claude.

Why does Claude not call my connector tools?

Claude chooses tools from the tool name, description, and input schema. If a description is vague, overloaded, or missing the return shape, Claude may answer in text instead of calling the tool. Use specific tool names, describe when to call the tool, state what it returns, keep schemas narrow, and disable unrelated connectors during debugging.

Why is my Claude Connector UI not rendering?

For an MCP App UI to render, the tool must link to a UI resource and return structuredContent that matches what the resource expects. Check that the resource is declared, the resource URI or sunpeak resource name matches the tool config, the production bundle builds, and the component handles loading, error, empty, and cancelled states. Browser DevTools usually show the client-side exception.

How do I fix Claude Connector OAuth errors?

Claude hosted surfaces use the OAuth callback https://claude.ai/api/mcp/auth_callback. Your protected resource metadata and authorization server metadata must be publicly reachable, tokens must use the MCP server as the audience, and PKCE S256 must be supported. If the MCP endpoint includes a path like /mcp, include that path in the protected resource metadata well-known URL.

What output size limits should Claude Connector tools follow?

Anthropic documents different limits by surface: Claude.ai and Claude Desktop allow about 150,000 characters per tool result, while Claude Code defaults to 25,000 tokens and can be configured with MAX_MCP_OUTPUT_TOKENS. Treat these as upper bounds, not targets. Paginate, summarize, and return only the fields the model and UI need.

How do I fix Claude Connector timeout errors?

Claude.ai and Claude Desktop tool calls time out after 300 seconds. Most tools should finish much faster. Add timeouts to outbound fetches, cache repeated reads, split long-running operations into start/status tools, and return progress or a queued job ID instead of holding the request open.

Can I use console.log to debug a Claude Connector?

Yes. Server-side logs appear wherever your MCP server runs. Client-side logs from MCP App resources appear in browser DevTools for the iframe context. In sunpeak, server logs are in your terminal and resource logs are in the browser console, which makes it easier to separate backend tool failures from frontend rendering failures.