> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fish.audio/llms.txt
> Use this file to discover all available pages before exploring further.

# AI Coding Agents

> Connect AI coding assistants to Fish Audio documentation via MCP for real-time API guidance

export const AudioTranscript = ({voices, page}) => {
  const resolvedVoices = voices?.length ? voices : (() => {
    if (!page) return [];
    const baseUrl = 'https://pub-b995142090474379a930b856ab79b4d4.r2.dev/audio';
    const pageVoices = [{
      id: '8ef4a238714b45718ce04243307c57a7',
      name: 'E-girl'
    }, {
      id: '802e3bc2b27e49c2995d23ef70e6ac89',
      name: 'Energetic Male'
    }, {
      id: '933563129e564b19a115bedd57b7406a',
      name: 'Sarah'
    }, {
      id: 'bf322df2096a46f18c579d0baa36f41d',
      name: 'Adrian'
    }, {
      id: 'b347db033a6549378b48d00acb0d06cd',
      name: 'Selene'
    }, {
      id: '536d3a5e000945adb7038665781a4aca',
      name: 'Ethan'
    }];
    return pageVoices.map(voice => ({
      ...voice,
      url: `${baseUrl}/${page}/${voice.id}.mp3`
    }));
  })();
  const [selectedVoice, setSelectedVoice] = useState(0);
  const [isPlaying, setIsPlaying] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(0);
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
  const audioRef = useRef(null);
  const dropdownRef = useRef(null);
  useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;
    const updateTime = () => setCurrentTime(audio.currentTime);
    const updateDuration = () => setDuration(audio.duration);
    const handleEnded = () => setIsPlaying(false);
    audio.addEventListener('timeupdate', updateTime);
    audio.addEventListener('loadedmetadata', updateDuration);
    audio.addEventListener('ended', handleEnded);
    return () => {
      audio.removeEventListener('timeupdate', updateTime);
      audio.removeEventListener('loadedmetadata', updateDuration);
      audio.removeEventListener('ended', handleEnded);
    };
  }, []);
  useEffect(() => {
    const handleClickOutside = event => {
      if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
        setIsDropdownOpen(false);
      }
    };
    if (isDropdownOpen) {
      document.addEventListener('mousedown', handleClickOutside);
    }
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, [isDropdownOpen]);
  useEffect(() => {
    if (audioRef.current) {
      audioRef.current.pause();
      audioRef.current.load();
      setIsPlaying(false);
      setCurrentTime(0);
    }
  }, [selectedVoice]);
  const togglePlay = () => {
    if (isPlaying) {
      audioRef.current.pause();
    } else {
      audioRef.current.play();
    }
    setIsPlaying(!isPlaying);
  };
  const handleProgressChange = e => {
    const newTime = parseFloat(e.target.value);
    audioRef.current.currentTime = newTime;
    setCurrentTime(newTime);
  };
  const formatTime = time => {
    if (isNaN(time)) return '0:00';
    const minutes = Math.floor(time / 60);
    const seconds = Math.floor(time % 60);
    return `${minutes}:${seconds.toString().padStart(2, '0')}`;
  };
  const currentVoice = resolvedVoices[selectedVoice];
  return <div className="border rounded-lg bg-card border-gray-200 dark:border-gray-800">
      {}
      <div className="grid grid-cols-3 items-center px-3 py-1.5 bg-muted border-b border-gray-200 dark:border-gray-800">
        <span className="text-xs font-medium">Listen to Page</span>

        <span className="text-xs font-semibold text-muted-foreground text-center">Powered by Fish Audio S2 Pro</span>

        {resolvedVoices.length > 1 ? <div className="relative justify-self-end" ref={dropdownRef}>
            <button onClick={() => setIsDropdownOpen(!isDropdownOpen)} className="flex items-center gap-1.5 px-3 py-1 rounded-full bg-muted hover:bg-gray-200 dark:hover:bg-gray-700 transition-all duration-200 cursor-pointer text-xs">
              <span className="text-muted-foreground">Voice:</span>
              <span className="font-medium">{resolvedVoices[selectedVoice]?.name}</span>
              <svg className={`w-3 h-3 transition-transform duration-200 ${isDropdownOpen ? 'rotate-180' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
              </svg>
            </button>

            {isDropdownOpen && <div className="absolute right-0 mt-1 w-auto bg-white dark:bg-black border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden z-50">
                {resolvedVoices.map((voice, index) => <button key={index} onClick={() => {
    setSelectedVoice(index);
    setIsDropdownOpen(false);
  }} className={`w-full px-3 py-1.5 text-left text-xs hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors flex items-center gap-2 ${index === selectedVoice ? 'bg-gray-100 dark:bg-gray-800 font-medium' : ''}`}>
                    {voice.id && <img src={`https://public-platform.r2.fish.audio/coverimage/${voice.id}`} alt={voice.name} className="w-5 h-5 rounded-full m-0 flex-shrink-0 object-cover" />}
                    <span className="flex-1 whitespace-nowrap">{voice.name}</span>
                  </button>)}
              </div>}
          </div> : <div className="justify-self-end" />}
      </div>

      {}
      <div className="px-3 py-1.5 bg-card">
        <audio ref={audioRef} src={currentVoice?.url} preload="metadata" />

        <div className="flex items-center gap-2">
          {}
          <button onClick={togglePlay} className="flex-shrink-0 w-6 h-6 flex items-center justify-center bg-gray-300 dark:bg-gray-600 text-gray-800 dark:text-gray-200 rounded-full hover:opacity-80 transition-opacity relative overflow-hidden" aria-label={isPlaying ? 'Pause' : 'Play'}>
            <div className="transition-transform duration-300 ease-in-out" style={{
    transform: isPlaying ? 'rotate(180deg)' : 'rotate(0deg)'
  }}>
              {isPlaying ? <svg className="w-3 h-3" fill="currentColor" viewBox="0 0 24 24">
                  <path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z" />
                </svg> : <svg className="w-3 h-3 ml-0.5" fill="currentColor" viewBox="0 0 24 24">
                  <path d="M8 5v14l11-7z" />
                </svg>}
            </div>
          </button>

          {}
          <div className="flex-1 flex items-center gap-2">
            <span className="text-xs font-mono text-gray-500 dark:text-gray-400 min-w-[35px]">
              {formatTime(currentTime)}
            </span>

            <div className="flex-1 relative h-1 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
              <div className="absolute top-0 left-0 h-full bg-gray-400 dark:bg-gray-500 transition-all duration-100" style={{
    width: `${duration ? currentTime / duration * 100 : 0}%`
  }} />
              <input type="range" min="0" max={duration || 0} value={currentTime} onChange={handleProgressChange} className="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer" />
            </div>
            <span className="text-xs font-mono text-gray-500 dark:text-gray-400 min-w-[35px]">
              {formatTime(duration)}
            </span>
          </div>
        </div>
      </div>
    </div>;
};

## Overview

Integrate Fish Audio's comprehensive documentation directly into your AI coding assistants. Using MCP (Model Context Protocol), coding agents like Claude Code, Cursor, and Windsurf can access our latest API references, guides, and examples in real-time.

<Tip>
  The Fish Audio MCP server provides instant access to:

  * Complete API documentation
  * SDK usage examples
  * Best practices and implementation patterns
  * Troubleshooting guides

  Connect once and get accurate, up-to-date Fish Audio knowledge in your coding environment.
</Tip>

## Why Use MCP Integration?

<CardGroup cols={3}>
  <Card title="Real-Time Docs" icon="clock">
    Access the latest API documentation without leaving your editor
  </Card>

  <Card title="Accurate Code" icon="check">
    Generate working code based on current API specifications
  </Card>

  <Card title="Smart Assistance" icon="brain">
    Get context-aware help for debugging and optimization
  </Card>
</CardGroup>

## Setup

<Tabs>
  <Tab title="Claude Code">
    <Steps>
      <Step title="Run Installation Command">
        Open your terminal in your project directory and run:

        ```bash theme={null}
        claude mcp add --transport http fish-audio --scope project https://docs.fish.audio/mcp
        ```

        This creates a `.mcp.json` file in your project root with the Fish Audio documentation server configuration.

        <Accordion title="Understanding Installation Scopes">
          Claude Code supports three installation scopes:

          * **`--scope project`** (recommended): Stores configuration in `.mcp.json` at project root. Version-controlled and shared with your team.
          * **`--scope user`**: Available globally across all your projects, but private to your account.
          * **`--scope local`** (default): Project-specific but private to you only. Good for experimentation.

          For team collaboration, use project scope and commit the `.mcp.json` file to git.
        </Accordion>
      </Step>

      <Step title="Verify Connection">
        Check that the server is connected:

        ```bash theme={null}
        claude mcp list
        ```

        You should see `fish-audio` in the list of configured servers.
      </Step>

      <Step title="Test Integration">
        Ask Claude Code: "What Fish Audio models are available?" or "How do I use Fish Audio's TTS API?"
      </Step>
    </Steps>
  </Tab>

  <Tab title="Cursor">
    <Steps>
      <Step title="Open MCP Settings">
        Use `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Windows/Linux) to open the command palette, then search for "Open MCP settings".
      </Step>

      <Step title="Add Custom MCP Server">
        Select "Add custom MCP" to open the `mcp.json` configuration file.
      </Step>

      <Step title="Configure Fish Audio Server">
        Add the Fish Audio documentation server:

        ```json theme={null}
        {
          "mcpServers": {
            "fish-audio": {
              "url": "https://docs.fish.audio/mcp"
            }
          }
        }
        ```
      </Step>

      <Step title="Save and Reload">
        Save the configuration file and reload Cursor to apply changes.
      </Step>

      <Step title="Test Integration">
        In Cursor's chat, ask: "What tools do you have available?" You should see the Fish Audio MCP server listed. Then try: "What Fish Audio TTS models are available?"
      </Step>
    </Steps>

    <Note>
      Cursor's MCP support was added in early 2025. Ensure you're running the latest version for full functionality.
    </Note>
  </Tab>

  <Tab title="Windsurf">
    <Steps>
      <Step title="Access MCP Configuration">
        Go to `File > Preferences > Windsurf Settings`, then navigate to `Cascade > Model Context Protocol (MCP) Servers`.
      </Step>

      <Step title="Add Custom Server">
        Click "Add custom server +" or "View raw config" to edit the configuration file at `~/.codeium/windsurf/mcp_config.json`.
      </Step>

      <Step title="Configure Fish Audio Server">
        Add the Fish Audio documentation server:

        ```json theme={null}
        {
          "mcpServers": {
            "fish-audio": {
              "url": "https://docs.fish.audio/mcp"
            }
          }
        }
        ```
      </Step>

      <Step title="Save and Refresh">
        Save the configuration and click the refresh button in Windsurf to apply changes.
      </Step>

      <Step title="Test Integration">
        Open Cascade chat (Ctrl+L) and ask: "Search Fish Audio docs for TTS API usage" or "What emotion parameters does Fish Audio support?"
      </Step>
    </Steps>

    <Note>
      Windsurf's MCP support was introduced in Wave 3 (February 2025). Ensure you're running the latest version.
    </Note>
  </Tab>
</Tabs>

## Using the Integration

### Example Queries

Once connected, ask your coding agent questions naturally:

<CardGroup cols={2}>
  <Card title="Authentication" icon="key">
    "How do I authenticate with Fish Audio API?"
  </Card>

  <Card title="TTS Example" icon="microphone">
    "Show me Python code for text-to-speech"
  </Card>

  <Card title="Emotions" icon="masks-theater">
    "What emotion parameters are available?"
  </Card>

  <Card title="WebSocket" icon="plug">
    "Help me implement real-time streaming"
  </Card>
</CardGroup>

### Code Generation Examples

<Tabs>
  <Tab title="Basic TTS">
    Ask: "Generate a Python function for text-to-speech with Fish Audio"

    ```python theme={null}
    from fish_audio import FishAudioClient

    def text_to_speech(text: str, voice_id: str, output_file: str):
        """Convert text to speech using Fish Audio API"""
        client = FishAudioClient(api_key="your-api-key")

        response = client.tts.create(
            text=text,
            model_id=voice_id,
            format="mp3"
        )

        with open(output_file, "wb") as f:
            f.write(response.audio_data)

        return output_file
    ```
  </Tab>

  <Tab title="Voice Cloning">
    Ask: "Create a voice cloning pipeline with error handling"

    ```python theme={null}
    from fish_audio import FishAudioClient
    import logging

    def clone_voice(audio_path: str, name: str):
        """Clone a voice from audio sample"""
        client = FishAudioClient(api_key="your-api-key")

        try:
            # Upload audio sample
            with open(audio_path, "rb") as f:
                model = client.models.create(
                    name=name,
                    audio_data=f.read(),
                    description="Custom cloned voice"
                )

            logging.info(f"Voice cloned: {model.id}")
            return model.id

        except Exception as e:
            logging.error(f"Cloning failed: {e}")
            raise
    ```
  </Tab>

  <Tab title="Streaming">
    Ask: "Implement real-time TTS streaming"

    ```python theme={null}
    from fish_audio import FishAudioClient
    import asyncio

    async def stream_tts(text: str, voice_id: str):
        """Stream TTS audio in real-time"""
        client = FishAudioClient(api_key="your-api-key")

        async for chunk in client.tts.stream(
            text=text,
            model_id=voice_id,
            chunk_size=1024
        ):
            # Process audio chunk
            yield chunk
    ```
  </Tab>
</Tabs>

## Available Documentation

Your coding agent can access:

<CardGroup cols={3}>
  <Card title="API Reference" icon="code">
    Complete endpoint documentation with parameters
  </Card>

  <Card title="SDK Guides" icon="book">
    Python SDK usage and examples
  </Card>

  <Card title="Best Practices" icon="lightbulb">
    Optimization patterns and tips
  </Card>

  <Card title="Models & Pricing" icon="tags">
    Available models and rate limits
  </Card>

  <Card title="Voice Cloning" icon="copy">
    Custom voice creation guides
  </Card>

  <Card title="Troubleshooting" icon="wrench">
    Common issues and solutions
  </Card>
</CardGroup>

## Advanced Usage

### Custom Commands

Create agent workflows for common tasks:

<CodeGroup>
  ```text Voice Pipeline theme={null}
  "Create a complete voice generation pipeline with:
  - Authentication
  - Voice selection
  - Emotion control
  - Error handling
  - Audio export"
  ```

  ```text Batch Processing theme={null}
  "Build a batch TTS processor that:
  - Reads from CSV
  - Handles rate limits
  - Retries on failure
  - Tracks progress"
  ```

  ```text WebSocket Client theme={null}
  "Implement a WebSocket client for:
  - Real-time streaming
  - Auto-reconnection
  - Buffer management
  - Error recovery"
  ```
</CodeGroup>

### Context-Aware Features

With MCP integration, your agent can:

* Suggest appropriate models based on use case
* Handle rate limiting automatically
* Provide inline documentation
* Validate API calls against specifications
* Recommend optimization strategies

## Troubleshooting

<AccordionGroup>
  <Accordion title="Connection Issues">
    If the MCP server isn't connecting:

    1. Verify internet connectivity
    2. Check `https://docs.fish.audio/mcp` is accessible
    3. Ensure your agent supports MCP protocol
    4. Restart your coding environment
    5. Clear any cached configurations
  </Accordion>

  <Accordion title="Outdated Information">
    The MCP server always serves the latest documentation:

    1. Refresh the MCP connection in settings
    2. Clear documentation cache if available
    3. Report persistent issues to [support@fish.audio](mailto:support@fish.audio)
  </Accordion>

  <Accordion title="Missing Features">
    If certain features aren't available:

    1. Verify you're using the latest agent version
    2. Check MCP protocol compatibility
    3. Ensure proper server configuration
    4. Contact support for assistance
  </Accordion>
</AccordionGroup>

## Security

<Info>
  **Your data is safe:**

  * MCP provides read-only access to public documentation
  * No API keys are transmitted through MCP
  * All connections use HTTPS encryption
  * No user queries or usage data is stored
</Info>

## Next Steps

<CardGroup cols={2}>
  <Card title="API Introduction" icon="play" href="/api-reference/introduction">
    Start with Fish Audio API basics
  </Card>

  <Card title="Python SDK" icon="python" href="/developer-guide/sdk-guide/python/installation">
    Install and configure the Python SDK
  </Card>

  <Card title="TTS Guide and Best Practices" icon="microphone" href="/developer-guide/core-features/text-to-speech">
    Learn text-to-speech optimization
  </Card>

  <Card title="Voice Cloning Guide" icon="copy" href="/developer-guide/core-features/creating-models">
    Create custom voice models
  </Card>
</CardGroup>

## Support

Need help with MCP integration?

* **Technical Support**: [support@fish.audio](mailto:support@fish.audio)
* **Documentation Issues**: [GitHub](https://github.com/fishaudio)
* **Community**: [Discord](https://discord.gg/dF9Db2Tt3Y)
