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

# Connect an MCP client

> Connect Claude Code, Cursor, Codex, or any MCP client to a ModuleX MCP server: the endpoint URL, Streamable HTTP transport, server-key authentication, ready-to-paste client configs, and the full reference for the built-in and workflow tools a client can call.

export const MediaEmbed = ({id, type = 'screenshot', caption = '', ext, ratio = '16 / 9'}) => {
  const isVideo = type === 'video' || type === 'app_video';
  const resolvedExt = ext || (isVideo ? 'mp4' : type === 'screenshot' ? 'webp' : 'svg');
  const src = 'https://media.modulex.dev/' + id + '.' + resolvedExt;
  const [status, setStatus] = useState('loading');
  const [isDev, setIsDev] = useState(false);
  const [inView, setInView] = useState(false);
  const boxRef = useRef(null);
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const h = window.location.hostname;
    setIsDev(h === 'localhost' || h === '127.0.0.1' || h.endsWith('.mintlify.app'));
  }, []);
  useEffect(() => {
    if (inView) return;
    if (typeof IntersectionObserver === 'undefined') {
      setInView(true);
      return;
    }
    const el = boxRef.current;
    if (!el) return;
    const io = new IntersectionObserver(entries => {
      if (entries.some(e => e.isIntersecting)) {
        setInView(true);
        io.disconnect();
      }
    }, {
      rootMargin: '300px'
    });
    io.observe(el);
    return () => io.disconnect();
  }, [inView]);
  if (status === 'missing') {
    if (!isDev) return null;
    return <div style={{
      display: 'flex',
      flexDirection: 'column',
      justifyContent: 'center',
      gap: '0.4rem',
      padding: '1rem 1.25rem',
      margin: '1.25rem 0',
      width: '100%',
      aspectRatio: ratio,
      boxSizing: 'border-box',
      border: '1px dashed rgba(128,128,128,0.45)',
      borderRadius: '0.75rem',
      background: 'rgba(128,128,128,0.06)',
      color: 'currentColor',
      fontSize: '0.85rem',
      lineHeight: 1.45
    }}>
        <div style={{
      display: 'flex',
      alignItems: 'center',
      gap: '0.5rem',
      opacity: 0.75
    }}>
          <span aria-hidden="true">🎬</span>
          <code style={{
      fontSize: '0.75rem'
    }}>{id}</code>
          <span style={{
      fontSize: '0.65rem',
      textTransform: 'uppercase',
      letterSpacing: '0.04em',
      padding: '0.1rem 0.4rem',
      borderRadius: '0.4rem',
      background: 'rgba(128,128,128,0.18)'
    }}>
            {type}
          </span>
        </div>
        <div style={{
      opacity: 0.9
    }}>{caption || 'Media not uploaded yet.'}</div>
        <div style={{
      fontSize: '0.7rem',
      opacity: 0.5
    }}>
          Upload to R2 as <code>{id}.{resolvedExt}</code> — preview only, hidden in production.
        </div>
      </div>;
  }
  const mediaStyle = {
    display: status === 'loaded' ? 'block' : 'none',
    width: '100%',
    height: 'auto',
    borderRadius: '0.75rem'
  };
  const media = isVideo ? <video src={inView ? src : undefined} autoPlay loop muted playsInline preload="metadata" onLoadedData={() => setStatus('loaded')} onError={() => setStatus('missing')} style={mediaStyle} /> : <img src={inView ? src : undefined} alt={caption} onLoad={() => setStatus('loaded')} onError={() => setStatus('missing')} style={mediaStyle} />;
  return <figure style={{
    margin: '1.25rem 0'
  }}>
      <div ref={boxRef} style={status === 'loaded' ? {
    width: '100%'
  } : {
    width: '100%',
    aspectRatio: ratio,
    borderRadius: '0.75rem',
    background: 'rgba(128,128,128,0.06)'
  }}>
        {media}
      </div>
      {status === 'loaded' && caption ? <figcaption style={{
    marginTop: '0.5rem',
    textAlign: 'center',
    fontSize: '0.85rem',
    opacity: 0.7
  }}>
          {caption}
        </figcaption> : null}
    </figure>;
};

Once you have [published a server](/api-reference/mcp/publish-a-server) and a key, connecting a client takes two values: the server's **endpoint URL** and an active **key**. This page has the endpoint and transport details, the authentication header, ready-to-paste configs for common clients, and the reference for every tool a connected client can call.

## Endpoint and transport

A published server has a single endpoint, shown on its **Overview** and **Connect** tabs:

```text Endpoint theme={null}
https://api.modulex.dev/mcp/serve/{server_id}
```

The server speaks the **Model Context Protocol over Streamable HTTP**: JSON-RPC requests sent as HTTP `POST` with `Content-Type: application/json` and `Accept: application/json`. It is stateless — each request stands alone — so there is no session to open or tear down. MCP clients handle the protocol for you; you only supply the URL and key, and the client discovers the available tools and calls them.

## Authentication

A client authenticates with the server's [key](/api-reference/mcp/publish-a-server#server-keys) (prefix `mx_mcp_`), sent as an `X-API-Key` header:

```text Request header theme={null}
X-API-Key: mx_mcp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

Clients that send credentials as a bearer token can use `Authorization` instead:

```text Bearer alternative theme={null}
Authorization: Bearer mx_mcp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

The key is all a client sends. The server already knows its organization from the endpoint URL, so **do not add an `X-Organization-ID` header** — an MCP client authenticates with the server key alone. This is different from the ModuleX REST API, which uses an `mx_live_` key plus `X-Organization-ID`; see [API authentication](/api-reference/authentication) for that surface.

## Client configuration

Copy the config for your client from the server's **Connect** tab, or use the templates below. Each uses the environment variable `MODULEX_MCP_KEY` for the key so you keep the secret out of the config file where the client supports it.

<MediaEmbed id="MX-MEDIA-4740" type="screenshot" caption={"The Connect tab of a published server: tabbed, ready-to-paste configuration for Claude Code, Cursor, Codex, and a generic HTTP client, each pre-filled with the server URL."} />

<Tabs>
  <Tab title="Claude Code">
    Add the server to your MCP client configuration:

    ```json Claude Code theme={null}
    {
      "mcpServers": {
        "modulex": {
          "type": "http",
          "url": "https://api.modulex.dev/mcp/serve/8f2c1d4b-1a2b-4c3d-9e8f-6a5b4c3d2e1f",
          "headers": { "X-API-Key": "${MODULEX_MCP_KEY}" }
        }
      }
    }
    ```

    Set the key in your environment:

    ```bash Set the key theme={null}
    export MODULEX_MCP_KEY='mx_mcp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    ```

    See the [Claude Code MCP docs](https://docs.anthropic.com/en/docs/claude-code/mcp) for where the config lives.
  </Tab>

  <Tab title="Cursor">
    Add the server to your MCP configuration:

    ```json Cursor theme={null}
    {
      "mcpServers": {
        "modulex": {
          "url": "https://api.modulex.dev/mcp/serve/8f2c1d4b-1a2b-4c3d-9e8f-6a5b4c3d2e1f",
          "headers": { "X-API-Key": "mx_mcp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
        }
      }
    }
    ```

    See the [Cursor MCP docs](https://docs.cursor.com/context/model-context-protocol) for the config location.
  </Tab>

  <Tab title="Codex">
    Add the server to your Codex configuration:

    ```toml Codex theme={null}
    [mcp_servers.modulex]
    url = "https://api.modulex.dev/mcp/serve/8f2c1d4b-1a2b-4c3d-9e8f-6a5b4c3d2e1f"
    env_http_headers = { "X-API-Key" = "MODULEX_MCP_KEY" }
    tool_timeout_sec = 180
    ```

    Set `MODULEX_MCP_KEY` in your environment. See the [Codex MCP docs](https://learn.chatgpt.com/docs/extend/mcp) for details.
  </Tab>

  <Tab title="Generic HTTP">
    Any MCP client that supports Streamable HTTP can connect with the URL and key:

    ```json Generic HTTP MCP client theme={null}
    {
      "transport": "streamable-http",
      "url": "https://api.modulex.dev/mcp/serve/8f2c1d4b-1a2b-4c3d-9e8f-6a5b4c3d2e1f",
      "headers": { "X-API-Key": "mx_mcp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
    }
    ```

    If your client sends bearer tokens rather than a custom header, use `"Authorization": "Bearer mx_mcp_…"` instead of `X-API-Key`. See the [MCP transports spec](https://modelcontextprotocol.io/docs/concepts/transports).
  </Tab>
</Tabs>

<Note>
  Replace the example URL with your server's endpoint (from its Overview tab) and the placeholder with a real key. The `modulex` label is the name the client shows for the server — change it freely.
</Note>

<MediaEmbed id="MX-MEDIA-4750" type="app_video" caption={"A short walkthrough: copy the endpoint and key from the Connect tab, paste them into an MCP client, and watch the ModuleX workflow and Knowledge tools appear and run."} />

## The published tools

A connected client lists the server's tools automatically. What it sees depends on which tools you [enabled when publishing](/api-reference/mcp/publish-a-server#create-a-server): a fixed set of built-in tools, plus one tool per published workflow.

### Built-in tools

The built-in tools cover your organization's Files and Knowledge. Enable the ones you want per server.

| Tool                     | Name                       | What it does                                                                                  |
| ------------------------ | -------------------------- | --------------------------------------------------------------------------------------------- |
| List available workflows | `workflows_list_available` | Lists the workflow tools this server publishes.                                               |
| List files               | `files_list`               | Lists your organization's Files.                                                              |
| Get file content         | `files_get_content`        | Reads the content of a File.                                                                  |
| Write text file          | `files_write_text`         | Writes a text File.                                                                           |
| Append text file         | `files_append_text`        | Appends text to a File.                                                                       |
| List knowledge           | `knowledge_list`           | Lists your organization's Knowledge bases.                                                    |
| Search knowledge         | `knowledge_search`         | Runs [retrieval](/concepts/knowledge-rag) over a Knowledge base and returns matching context. |

<Info>
  Enabled built-in tools are available to every active key on the server. **File writes** count against your organization's existing Files quotas, and **Search knowledge** consumes [credits](/billing/credits) like any managed retrieval.
</Info>

### Workflow tools

Each workflow you publish becomes one tool:

* The **tool name and description** come from the workflow's published metadata.
* The **input schema** is the workflow's published inputs, so the client knows what arguments to pass.
* **Calling the tool runs the deployed workflow** with those inputs and returns its result. This consumes [credits](/billing/credits) exactly as running the workflow in the app does.

Publish and deploy workflows from the [builder](/workflow-builder/execution/deploy); a client sees the current set the next time it lists tools.

## Troubleshoot a connection

<AccordionGroup>
  <Accordion title="The client shows no tools or fails to connect">
    Confirm the endpoint URL is the server's own (from its **Overview** tab), that the key is active and not expired, and that the client is configured for **Streamable HTTP**. Rotate the key from the **Keys** tab if you are unsure it is current.
  </Accordion>

  <Accordion title="A tool call returns an error instead of running">
    Calls that run a workflow or search Knowledge are billed. If your organization is out of [credits](/billing/credits) or its plan does not permit the usage, the call returns an error result. See [Usage gating & limits](/billing/usage-gating).
  </Accordion>

  <Accordion title="A workflow tool is missing">
    Only [deployed](/workflow-builder/execution/deploy) workflows that the server publishes appear. Deploy the workflow and confirm it is included in the server's published tools, then re-list tools in the client.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Publish an MCP server" icon="upload" href="/api-reference/mcp/publish-a-server">
    Create servers, choose tools, and manage keys and limits.
  </Card>

  <Card title="ModuleX MCP overview" icon="circle-info" href="/api-reference/mcp/overview">
    What ModuleX MCP is and how it differs from custom MCP servers.
  </Card>

  <Card title="Deploy a workflow" icon="rocket" href="/workflow-builder/execution/deploy">
    Deploy a workflow so it can be published as an MCP tool.
  </Card>

  <Card title="Credits & metering" icon="credit-card" href="/billing/credits">
    How workflow runs and Knowledge search are metered.
  </Card>
</CardGroup>
