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

# Hosted Stdio-based MCP Server

> Run a hosted MCP server via stdio and expose it through the AI Gateway.

## What is Hosted Stdio-based MCP Server?

Run a hosted MCP server via stdio and expose it through the AI Gateway.

<Tip>
  Recommended for local development in IDEs only.
</Tip>

Hosted stdio MCP servers are now available as first-class MCP servers with built-in **per-user** or **global** credential support via environment variables.

A **hosted stdio-based MCP server** speaks the MCP over standard input and standard output (typical for CLI-style servers started with `npx`, `uvx`, or your own binary). TrueFoundry registers this pattern as a first-class MCP server: you provide the **command**, **arguments**, and **environment variables** for configuration and credentials, and the MCP Gateway runs and manages the process so clients use the same gateway URL as for [remote MCP servers](/docs/ai-gateway/mcp/mcp-server-getting-started).

This is useful when you already rely on a stdio MCP package or script and want centralized access, auth, guardrails, and observability without standing up a separate HTTP wrapper yourself.

## How It Works?

| Element                   | Role                                                                                                                                                                                                                                                                                                |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Command**               | Executable the AI Gateway runs to start the MCP server (for example `npx` or `uvx`)                                                                                                                                                                                                                 |
| **Arguments**             | Arguments passed to that executable (for example package name and flags)                                                                                                                                                                                                                            |
| **Environment variables** | Injected into the server process; use shared values for **global** credentials, or **per-user** values that include **one** templatized credential placeholder per variable—substituted from [Auth Overrides](/docs/ai-gateway/mcp/mcp-server-auth-overrides), same model as Individual Credentials |
| **Collaborators**         | Same access model as other MCP servers ([Getting Started](/docs/ai-gateway/mcp/mcp-server-getting-started))                                                                                                                                                                                         |

You configure **environment variables** on the server (names and values). For **per-user** credentials, each value may include **one** templatized placeholder that the AI Gateway resolves from that caller’s [Auth Overrides](/docs/ai-gateway/mcp/mcp-server-auth-overrides)—the same substitution behavior as Individual Credentials on remote MCP servers (see [MCP Gateway Auth & Security](/docs/ai-gateway/mcp/mcp-gateway-auth-security)). Arbitrary or additional template syntax is not supported.

## Create a Hosted Stdio-based MCP Server

<Steps>
  <Step title="Navigate to MCP Servers">
    Open the **MCP Servers** section in the TrueFoundry AI Gateway and choose **Add Server**.
  </Step>

  <Step title="Choose hosted stdio registration">
    Select **Create a Hosted STDIO-based MCP Server**. This path configures a **command**, **arguments**, and **environment variables** instead of a remote MCP URL.
  </Step>

  <Step title="Import from editor-style JSON (optional)">
    Paste MCP configuration JSON as a **single-entry** `mcpServers` map—the same shape as in Cursor or VS Code MCP config: one server id with `command`, `args`, and optional `env`. Only **one** server entry is allowed so the control plane can build one manifest.
  </Step>

  <Step title="Set command and arguments">
    Configure the process the AI Gateway should run:

    | Field            | Description                                                                                                                                                                      |
    | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | **Name**         | Unique identifier for the MCP server (lowercase letters, numbers, and hyphens). Used in URLs and API references.                                                                 |
    | **Display Name** | Optional human-readable label shown on the MCP registry page - instead of the internal name. If omitted, **Name** is shown. You can also set this later when editing the server. |
    | **Command**      | Executable for the MCP server process (for example `npx`)                                                                                                                        |
    | **Arguments**    | List of arguments passed to the command                                                                                                                                          |
  </Step>

  <Step title="Configure environment variables and access">
    Add environment variables required by the server (API keys, feature flags, and so on). Choose **global** credentials when everyone shares the same downstream access, or **per-user** when a value uses **one** templatized placeholder filled from each caller’s [Auth Overrides](/docs/ai-gateway/mcp/mcp-server-auth-overrides). Add **collaborators** and roles as for any MCP server.
  </Step>

  <Step title="Create and test">
    Save the server, then use the **Playground** to list and invoke tools, and open **How To Use** for IDE and agent connection snippets.
  </Step>
</Steps>

## Example manifests

Each example shows the editor-style JSON you can paste into the **STDIO Configuration (JSON)** box when [adding the server in the UI](#create-a-hosted-stdio-based-mcp-server), plus the equivalent full YAML manifest (`type: mcp-server/stdio`). Both examples run reference servers from the official [modelcontextprotocol/servers](https://github.com/modelcontextprotocol/servers) repository and need no credentials. For servers that take API keys, add the variables under `env` (JSON) or **`auth_data.env`** (YAML).

The JSON carries only `command`, `args`, and `env`—after pasting, mark each environment variable as **global** or **per-user** and add collaborators in the form.

<Note>
  Replace the collaborator `subject` with a real user in your account. Do not commit real API keys—use a secret store or placeholder for **global** env values. For **per-user** env values, use **one** templatized placeholder per value and configure each caller in [Auth Overrides](/docs/ai-gateway/mcp/mcp-server-auth-overrides).
</Note>

<Tabs>
  <Tab title="Filesystem">
    Runs the official [filesystem server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem). The trailing argument is the directory the server is allowed to access—paths are inside the gateway sandbox, so tools operate on sandbox-local files. No credentials are needed, so `auth_data` is omitted.

    <CodeGroup>
      ```json STDIO Configuration (JSON) theme={"dark"}
      {
        "mcpServers": {
          "filesystem": {
            "command": "npx",
            "args": [
              "-y",
              "@modelcontextprotocol/server-filesystem",
              "/tmp"
            ]
          }
        }
      }
      ```

      ```yaml YAML manifest theme={"dark"}
      name: filesystem
      description: Official MCP filesystem reference server
      collaborators:
        - role_id: mcp-server-manager
          subject: user:you@example.com
      type: mcp-server/stdio
      command: npx
      args:
        - "-y"
        - "@modelcontextprotocol/server-filesystem"
        - /tmp
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Everything">
    Runs the official [everything server](https://github.com/modelcontextprotocol/servers/tree/main/src/everything), a test server that exercises prompts, tools, and resources across the MCP protocol. Useful for trying the **Playground** and client connections before registering your own server.

    <CodeGroup>
      ```json STDIO Configuration (JSON) theme={"dark"}
      {
        "mcpServers": {
          "everything": {
            "command": "npx",
            "args": [
              "-y",
              "@modelcontextprotocol/server-everything"
            ]
          }
        }
      }
      ```

      ```yaml YAML manifest theme={"dark"}
      name: everything
      description: Official MCP everything test server
      collaborators:
        - role_id: mcp-server-manager
          subject: user:you@example.com
      type: mcp-server/stdio
      command: npx
      args:
        - "-y"
        - "@modelcontextprotocol/server-everything"
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Using Private Packages

Stdio MCP servers can run packages that are not published to the public npm registry or PyPI. No extra gateway configuration is needed: the environment variables you set in `auth_data.env` are available to the package manager (`npx`, `uvx`) when it installs the package inside the sandbox, so registry and repository credentials flow the same way as any other credential.

<Warning>
  Never put tokens in `args`. Arguments are stored in plain text in the manifest and can appear in process listings. Keep every credential in `auth_data.env`.
</Warning>

Two patterns are supported:

| Pattern                                     | When to use                                                                                                         | Trade-offs                                                                                                                       |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **Private registry or index** (recommended) | Your package is published to a private npm registry (GitHub Packages, Artifactory, Azure Artifacts) or Python index | Requires publishing infrastructure; fastest installs and standard version pinning                                                |
| **Direct from a private git repository**    | No publishing infrastructure; the package lives only in a git repo                                                  | No registry needed; slower cold starts (clone + build) and a shell wrapper is required to keep the token out of the command line |

Registry credentials are usually **global** (one shared read token for installing the package), even when the server's own downstream credentials are **per-user**.

<Tabs>
  <Tab title="npm: private registry">
    Installs `@your-org/your-mcp-server` from GitHub Packages. The `--@your-org:registry` argument routes the scope to the private registry, and npm reads the token from the `npm_config_//<registry-host>/:_authToken` environment variable.

    <CodeGroup>
      ```json STDIO Configuration (JSON) theme={"dark"}
      {
        "mcpServers": {
          "internal-tools": {
            "command": "npx",
            "args": [
              "-y",
              "--@your-org:registry=https://npm.pkg.github.com",
              "@your-org/your-mcp-server@1.4.2"
            ],
            "env": {
              "npm_config_//npm.pkg.github.com/:_authToken": "your-registry-read-token"
            }
          }
        }
      }
      ```

      ```yaml YAML manifest theme={"dark"}
      name: internal-tools
      description: Stdio MCP server from a private npm registry
      collaborators:
        - role_id: mcp-server-manager
          subject: user:you@example.com
      type: mcp-server/stdio
      command: npx
      args:
        - "-y"
        - "--@your-org:registry=https://npm.pkg.github.com"
        - "@your-org/your-mcp-server@1.4.2"
      auth_data:
        type: env
        auth_level: global
        env:
          "npm_config_//npm.pkg.github.com/:_authToken": your-registry-read-token
      ```
    </CodeGroup>

    For Artifactory or Azure Artifacts, replace the registry URL in both the `--@your-org:registry` argument and the `npm_config_//<registry-host>/:_authToken` variable name with your registry's host and path.
  </Tab>

  <Tab title="npm: private git repository">
    Installs directly from a private GitHub repository. npm strips credentials embedded in package URLs during URL normalization, so the token must be injected through git's `insteadOf` URL rewriting before `npx` runs. The `sh -c` wrapper keeps the token in the environment only.

    <CodeGroup>
      ```json STDIO Configuration (JSON) theme={"dark"}
      {
        "mcpServers": {
          "internal-tools": {
            "command": "sh",
            "args": [
              "-c",
              "git config --global url.\"https://x-access-token:${GITHUB_TOKEN}@github.com/\".insteadOf \"https://github.com/\" && exec npx -y \"git+https://github.com/your-org/your-mcp-server.git#v1.4.2\""
            ],
            "env": {
              "GITHUB_TOKEN": "your-github-read-token"
            }
          }
        }
      }
      ```

      ```yaml YAML manifest theme={"dark"}
      name: internal-tools
      description: Stdio MCP server from a private git repository
      collaborators:
        - role_id: mcp-server-manager
          subject: user:you@example.com
      type: mcp-server/stdio
      command: sh
      args:
        - "-c"
        - >-
          git config --global
          url."https://x-access-token:${GITHUB_TOKEN}@github.com/".insteadOf
          "https://github.com/" &&
          exec npx -y "git+https://github.com/your-org/your-mcp-server.git#v1.4.2"
      auth_data:
        type: env
        auth_level: global
        env:
          GITHUB_TOKEN: your-github-read-token
      ```
    </CodeGroup>

    Always pin a ref with `#tag`, `#branch`, or `#commit`. Without it, `npx` fails during checkout and the server never starts.
  </Tab>

  <Tab title="Python: private index">
    Installs `your-mcp-server` with `uvx` from a private Python index. `UV_INDEX` adds the private index alongside PyPI so public dependencies still resolve; credentials are embedded in the index URL, which lives entirely in `auth_data.env`.

    <CodeGroup>
      ```json STDIO Configuration (JSON) theme={"dark"}
      {
        "mcpServers": {
          "internal-tools": {
            "command": "uvx",
            "args": [
              "your-mcp-server==1.4.2"
            ],
            "env": {
              "UV_INDEX": "https://ci-user:your-index-read-token@pypi.your-company.com/simple"
            }
          }
        }
      }
      ```

      ```yaml YAML manifest theme={"dark"}
      name: internal-tools
      description: Stdio MCP server from a private Python index
      collaborators:
        - role_id: mcp-server-manager
          subject: user:you@example.com
      type: mcp-server/stdio
      command: uvx
      args:
        - your-mcp-server==1.4.2
      auth_data:
        type: env
        auth_level: global
        env:
          UV_INDEX: https://ci-user:your-index-read-token@pypi.your-company.com/simple
      ```
    </CodeGroup>

    If your private index mirrors PyPI (common with Artifactory), use `UV_DEFAULT_INDEX` instead to route all resolution through it. `UV_INDEX_URL` is deprecated in current uv releases.
  </Tab>

  <Tab title="Python: private git repository">
    Runs a tool from a private GitHub repository with `uvx --from`. The same git `insteadOf` rewrite injects the token, and uv pins the ref with `@` (not `#` as in npm).

    <CodeGroup>
      ```json STDIO Configuration (JSON) theme={"dark"}
      {
        "mcpServers": {
          "internal-tools": {
            "command": "sh",
            "args": [
              "-c",
              "git config --global url.\"https://x-access-token:${GITHUB_TOKEN}@github.com/\".insteadOf \"https://github.com/\" && exec uvx --from \"git+https://github.com/your-org/your-mcp-server@v1.4.2\" your-mcp-server"
            ],
            "env": {
              "GITHUB_TOKEN": "your-github-read-token"
            }
          }
        }
      }
      ```

      ```yaml YAML manifest theme={"dark"}
      name: internal-tools
      description: Stdio MCP server from a private git repository
      collaborators:
        - role_id: mcp-server-manager
          subject: user:you@example.com
      type: mcp-server/stdio
      command: sh
      args:
        - "-c"
        - >-
          git config --global
          url."https://x-access-token:${GITHUB_TOKEN}@github.com/".insteadOf
          "https://github.com/" &&
          exec uvx --from "git+https://github.com/your-org/your-mcp-server@v1.4.2"
          your-mcp-server
      auth_data:
        type: env
        auth_level: global
        env:
          GITHUB_TOKEN: your-github-read-token
      ```
    </CodeGroup>

    The repository needs a `pyproject.toml` with a script entry point matching the tool name (`your-mcp-server` here).
  </Tab>
</Tabs>

### Troubleshooting private package installs

Install failures do not surface as errors: the server stays in the `mcp_server_starting` state indefinitely because the package manager never hands off to the MCP process. If your server is stuck starting, check these causes:

<AccordionGroup>
  <Accordion title="Git URL has no pinned ref">
    A git URL without `#ref` (npm) or `@ref` (uv) can make the install run `git checkout null` and hang. Always pin a tag, branch, or commit in the package URL.
  </Accordion>

  <Accordion title="Credentials embedded in the npm package URL">
    npm normalizes package URLs and strips embedded credentials, so `git+https://user:token@github.com/...` in `args` fails to authenticate. Inject the token with git's `insteadOf` URL rewriting from an environment variable, as shown in the private git repository examples.
  </Accordion>

  <Accordion title="A failed first install poisoned the npx cache">
    If the first `npx` run is killed or fails partway, the partial download in `~/.npm/_npx` inside the sandbox makes subsequent starts fail the same way. Change something in the manifest `args` (for example, pin a different ref or add a flag): this re-keys the sandbox and starts from a clean cache.
  </Accordion>

  <Accordion title="Server did not pick up a rotated token">
    Environment variable values are part of the sandbox identity. Updating a token in `auth_data.env` automatically creates a fresh sandbox with the new value on the next start; the old sandbox ages out. If the server still fails after rotation, verify the new token has read access to the package.
  </Accordion>
</AccordionGroup>

## Frequently Asked Questions

<AccordionGroup>
  <Accordion title="What MCP protocol features does the stdio proxy support?">
    Clients connect over streamable HTTP POST requests, and any MCP method your stdio server implements is forwarded. Standalone server-initiated notifications outside a request's response stream are not delivered. The gateway runs your server via the `mcp-proxy` library in a sandboxed environment in stateless mode, so the same per-request semantics as remote servers apply — see [MCP Protocol Support](/docs/ai-gateway/mcp/mcp-protocol-support) for the full feature matrix.
  </Accordion>

  <Accordion title="How is this different from a remote MCP server?">
    Remote servers point at an **HTTP (or streamable HTTP) MCP URL**. Stdio servers describe a **local process** (command + args) the AI Gateway runs. Pick remote when you already have a URL; pick stdio when your integration is packaged as a stdio CLI.
  </Accordion>

  <Accordion title="Can I combine a stdio MCP server with a Virtual MCP server?">
    Yes. After registration, you can include its tools in a [Virtual MCP Server](/docs/ai-gateway/mcp/virtual-mcp-server) alongside tools from OpenAPI or remote servers.
  </Accordion>

  <Accordion title="What templatized values are supported in environment variables?">
    For **per-user** credentials, **one** templatized placeholder per environment variable value is supported; the AI Gateway substitutes it using [Auth Overrides](/docs/ai-gateway/mcp/mcp-server-auth-overrides), consistent with Individual Credentials elsewhere. Multiple placeholders or other template syntax in the same value are not supported.
  </Accordion>

  <Accordion title="Can I run packages that are not publicly published?">
    Yes. Install from a private registry (npm or Python index) with the registry token in `auth_data.env`, or directly from a private git repository using a shell wrapper that injects the token through git configuration. See [Using Private Packages](#using-private-packages) for complete manifests and troubleshooting.
  </Accordion>

  <Accordion title="Do guardrails and analytics apply to stdio MCP servers?">
    Yes. Traffic through the MCP Gateway is subject to the same controls and observability patterns as other registered MCP server types, consistent with [OpenAPI-backed MCP servers](/docs/ai-gateway/mcp/openapi-mcp-server) and remote servers.
  </Accordion>
</AccordionGroup>

## Best Practices

<CardGroup cols={2}>
  <Card title="Pin versions" icon="tag">
    Prefer explicit package versions in `args` (for example pinned `npx` or `uvx` targets) so tool behavior does not change unexpectedly when upstream releases update.
  </Card>

  <Card title="Least-privilege environment variables" icon="shield">
    Pass only the environment variables the process needs, and use per-user auth when each caller should use their own downstream credentials.
  </Card>

  <Card title="Align with local dev" icon="code">
    Keep command and args aligned with a working local MCP config so debugging stays straightforward.
  </Card>

  <Card title="Scope private-package tokens" icon="key">
    For private registries and repositories, use a fine-grained token with read-only access scoped to the single package or repository the server installs—never a broad organization-wide token.
  </Card>
</CardGroup>
