# Host Configuration

Course: [Model Context Protocol](https://www.devtutorials.ai/learn/mcp) · Section: Server Development
Source: https://www.devtutorials.ai/learn/mcp/server-development/host-configuration

> Connecting MCP servers to hosts: stdio and HTTP entries, VS Code mcp.json with inputs and dev mode, Visual Studio config locations, servers vs mcpServers, keeping secrets out, and start-up checks.

When you set up a new printer, you tell your computer how to find it: what it is called, how it connects, and whether it needs a password. Once that is configured, your apps can use it without redoing the setup every time.

The same pattern applies to MCP servers. A host needs a small entry for each server: its name, how to reach it, and any needed settings or keys. Once the host has that information, it can start the server, connect to it, discover its tools, and let the model use them.

**The main idea:** host config is just the setup layer for MCP. A **stdio** server entry tells the host what command to run. An **HTTP** server entry tells it which URL to call. Secrets should come from prompts or environment variables, not from the config file itself. After that, the host must verify that the server starts and lists its tools correctly.

## A Real-World Example

Ravi wants the Brightgrid returns server inside VS Code so he can try it with the editor's AI chat.

- He adds `.vscode/mcp.json` to the project with two entries: a local build over **stdio** and the company cloud server over **HTTP**.
- The API key is not stored in the file. VS Code asks for it once and stores it safely.
- He runs **MCP: List Servers**, starts the local server, and checks the output log to confirm it launched and listed its tools.
- He commits the config file so teammates get the same setup, while each person supplies their own key.

**In simple words:** describe each server once, keep secrets out of the file, and prove the server starts before depending on it.

## What You Will Learn

In this lesson, you will learn:

1. What a host config contains for stdio and HTTP servers.
2. How to configure VS Code.
3. How to configure Visual Studio.
4. How to keep secrets out of config files.
5. How to check that a server starts, connects, and lists its tools.

The MCP Lab is also a kind of host, but it is set up in code to talk to a specific in-browser server. This lesson is about real hosts running on your machine, where the file-based config matters.

## What a Config Contains

The host needs enough information to either start a local process or contact a remote server. The exact fields depend on which type of server you are configuring.

### stdio Entries

For a local server, the host starts a program. A stdio entry usually includes:

- `type`: `"stdio"`
- `command`: the executable to run, such as `node`, `python`, or `npx`
- `args`: the arguments passed to that command, such as `["build/index.js"]`
- `env`: any environment variables the server needs
- sometimes `cwd` or `envFile` for more control over the startup environment

### HTTP Entries

For a remote server, the host calls a URL instead of running a local process. The entry usually includes:

- `type`: `"http"`
- `url`: the server endpoint, such as `https://mcp.brightgrid.example/mcp`
- `headers`: extra HTTP headers, such as an `Authorization` header with a token

## VS Code

### The File

VS Code reads MCP servers from a few places:

- **`.vscode/mcp.json`** in a workspace, with the top-level key **`servers`**. Commit this file when you want the team to share the same server setup.
- Your **user-level configuration**, opened with **MCP: Open User Configuration**, for servers you want in every project.
- A portable **`.mcp.json`** at the project root, which uses the key **`mcpServers`**. Many other hosts use the same shape.

```js runnable title="A .vscode/mcp.json file"
const config = {
  inputs: [
    {
      type: "promptString",
      id: "brightgrid-key",
      description: "Brightgrid API key",
      password: true,
    },
  ],
  servers: {
    "brightgrid-local": {
      type: "stdio",
      command: "node",
      args: ["build/index.js"],
      env: { BRIGHTGRID_API_KEY: "${input:brightgrid-key}" },
      dev: { watch: "build/**/*.js" },
    },
    "brightgrid-cloud": {
      type: "http",
      url: "https://mcp.brightgrid.example/mcp",
      headers: { Authorization: "Bearer ${input:brightgrid-key}" },
    },
  },
};
console.log(JSON.stringify(config, null, 2));
```

Expected output:

```text
{
  "inputs": [
    {
      "type": "promptString",
      "id": "brightgrid-key",
      "description": "Brightgrid API key",
      "password": true
    }
  ],
  "servers": {
    "brightgrid-local": {
      "type": "stdio",
      "command": "node",
      "args": [
        "build/index.js"
      ],
      "env": {
        "BRIGHTGRID_API_KEY": "${input:brightgrid-key}"
      },
      "dev": {
        "watch": "build/**/*.js"
      }
    },
    "brightgrid-cloud": {
      "type": "http",
      "url": "https://mcp.brightgrid.example/mcp",
      "headers": {
        "Authorization": "Bearer ${input:brightgrid-key}"
      }
    }
  }
}
```

### Inputs for Secrets

The `inputs` list tells the host which values to ask for. `"type": "promptString"` shows a prompt box, and `"password": true` hides what the user types. A reference like `${input:brightgrid-key}` then gets replaced at runtime. VS Code asks the first time the server starts and stores the answer for later use.

```js runnable title="How input placeholders are filled"
const answers = { "brightgrid-key": "sk-live-9f2c" };

function resolve(value) {
  return value.replace(
    /\$\{input:([\w-]+)\}/g,
    (_, id) => answers[id] ?? `<ask user for ${id}>`,
  );
}

const env = { BRIGHTGRID_API_KEY: "${input:brightgrid-key}", REGION: "eu" };
const resolved = Object.fromEntries(Object.entries(env).map(([k, v]) => [k, resolve(v)]));
console.log("in the file: ", JSON.stringify(env));
console.log(
  "at start-up:  BRIGHTGRID_API_KEY is",
  resolved.BRIGHTGRID_API_KEY ? "set" : "missing",
  "| REGION =",
  resolved.REGION,
);
console.log("unknown input:", resolve("${input:other-key}"));
```

Expected output:

```text
in the file:  {"BRIGHTGRID_API_KEY":"${input:brightgrid-key}","REGION":"eu"}
at start-up:  BRIGHTGRID_API_KEY is set | REGION = eu
unknown input: <ask user for other-key>
```

This is a common pattern because it keeps secrets out of source control while still making the value available to the server at runtime.

### Running and Debugging

- **MCP: List Servers** in the Command Palette lists configured servers and lets you start, stop, or restart them.
- The **output log** is the first place to check when a server fails to start.
- A `dev` section can enable watch mode; `dev.watch` can restart the server when matching files change, and VS Code can attach a debugger for Node.js or Python stdio servers.

## Visual Studio

Like VS Code, Visual Studio acts as the MCP host. The main difference is where each host stores its configuration files.

### Versions and Files

Visual Studio 2022 version 17.14 and Visual Studio 2026 support MCP servers for GitHub Copilot. Visual Studio looks for config files in several places, including:

- `%USERPROFILE%\.mcp.json`: personal config for all repositories
- `<solution>\.mcp.json`: repository-shared config
- `<solution>\.vs\mcp.json`: solution-local config for the current user
- `.vscode\mcp.json` under the solution folder

The file uses a top-level `servers` object, just like VS Code's. Editing it inside Visual Studio gives you validation and auto-completion, and saving the file reloads the server list.

## Formats Across Hosts

### servers vs mcpServers

Most hosts agree on the entries, but they do not always agree on the top-level key. VS Code and Visual Studio use `servers`, while the portable `.mcp.json` format uses `mcpServers`. Moving between them is usually just a matter of renaming the key.

```js runnable title="Convert between the two shapes"
const vscode = {
  servers: {
    brightgrid: { type: "stdio", command: "node", args: ["build/index.js"] },
    docs: { type: "http", url: "https://mcp.brightgrid.example/mcp" },
  },
};

function toPortable(cfg) {
  return { mcpServers: cfg.servers };
}

const portable = toPortable(vscode);
console.log("VS Code (.vscode/mcp.json): top key =", Object.keys(vscode)[0]);
console.log("Portable (.mcp.json):       top key =", Object.keys(portable)[0]);
console.log("servers:", Object.keys(portable.mcpServers).join(", "));
```

Expected output:

```text
VS Code (.vscode/mcp.json): top key = servers
Portable (.mcp.json):       top key = mcpServers
servers: brightgrid, docs
```

Check each host's documentation for the exact file name, location, and fields. Those details change faster than the protocol itself, and small differences, like `.mcp.json` versus `mcp.json`, matter.

## Keeping Secrets Out

### Rules

- Never write a key or token directly in a config file, especially if that file is committed to git.
- Use `${input:...}` prompts, environment variables, or an `envFile` that is not committed.
- Use `https://` for remote servers. Plain `http://` is fine only for local, trusted servers such as `http://127.0.0.1:3001/mcp`.

```js runnable title="Lint a config"
function lint(name, s) {
  const issues = [];
  if (s.type === "stdio" && !s.command) {
    issues.push("stdio needs a command");
  }
  if (s.type === "http") {
    if (!s.url) {
      issues.push("http needs a url");
    } else if (
      s.url.startsWith("http://") &&
      !/^http:\/\/(127\.0\.0\.1|localhost)/.test(s.url)
    ) {
      issues.push("use https for remote servers");
    }
  }
  const values = JSON.stringify({ env: s.env, headers: s.headers });
  if (/sk-[a-z]+-\w+|Bearer [A-Za-z0-9]{8,}/.test(values)) {
    issues.push("secret written in the file: use ${input:...}");
  }
  return `${name}: ${issues.length ? issues.join("; ") : "ok"}`;
}

console.log(lint("local", { type: "stdio", command: "node", args: ["build/index.js"] }));
console.log(lint("cloud", { type: "http", url: "http://mcp.brightgrid.example/mcp" }));
console.log(
  lint("leaky", { type: "stdio", command: "node", env: { KEY: "sk-live-9f2c" } }),
);
console.log(lint("dev", { type: "http", url: "http://127.0.0.1:3001/mcp" }));
```

Expected output:

```text
local: ok
cloud: use https for remote servers
leaky: secret written in the file: use ${input:...}
dev: ok
```

A config entry can start a command with your privileges. Only add servers from sources you trust, and read the command before starting it. A shared repository can add a server you never meant to run, so changes to an MCP config deserve the same review as code changes.

## Start-Up Checks

### Spawn, Connect, Discover

When a server is enabled, the host does a few basic checks in order:

1. **Start** it if it is a stdio server, or prepare the URL if it is an HTTP server.
2. **Connect** by sending a discovery request such as `server/discover` or, for older servers, `initialize`.
3. **Check** the versions and capabilities reported by the server.
4. **List** tools, resources, and prompts.

If a server fails at any step, the output log usually tells you why. For stdio servers, common problems are a wrong command, a missing environment variable, or a server that writes logs to stdout instead of stderr.

```js runnable title="A start-up checklist"
const steps = [
  ["start", "stdio: run the command | http: nothing to start", true],
  ["connect", "first request: server/discover (or initialize for older servers)", true],
  ["versions", "server supports 2026-07-28?", true],
  ["capabilities", "tools present?", true],
  ["tools/list", "expected tools: lookup_order, book_return", false],
];

for (const [step, check, ok] of steps) {
  console.log(`${ok ? "ok  " : "FAIL"} ${step.padEnd(13)} ${check}`);
  if (!ok) {
    console.log(
      "     -> open the server's output log; for stdio, read what it wrote to stderr",
    );
    break;
  }
}
```

Expected output:

```text
ok   start         stdio: run the command | http: nothing to start
ok   connect       first request: server/discover (or initialize for older servers)
ok   versions      server supports 2026-07-28?
ok   capabilities  tools present?
FAIL tools/list    expected tools: lookup_order, book_return
     -> open the server's output log; for stdio, read what it wrote to stderr
```

The pattern is the same everywhere: if the server does not show tools, the first place to look is the startup log.

## Common mistakes

- **Wrong top-level key.** Some hosts use `servers`, others use `mcpServers`. A wrong key means the host never sees the list.
- **Relative paths from the wrong folder.** Set `cwd` or use paths relative to the workspace.
- **Missing environment variables.** The server stops during startup. Read the log.
- **Using `http://` for a remote server.** Tokens go over the network in plain text. Use `https` instead.
- **Enabling too many servers at once.** Each server adds tools to the host's tool list. Only enable what the task needs.

## Exercises

Each exercise ends with a value on the last line. Press **Check** to compare it with the expected answer.

## What to Remember

- A host config lists servers. **stdio** entries use a command and usually arguments; **HTTP** entries use a URL and optional headers.
- **VS Code** uses `.vscode/mcp.json` with a top-level `servers` key, while the portable format uses `mcpServers`.
- Secrets should come from `${input:...}` prompts or environment variables, not from committed config files.
- Start-up checks are simple: start or connect, verify discovery, ensure the server advertises its capabilities, and inspect the output log when it fails.
- Review config changes as you would review code changes, because they can start commands with real privileges.

The pattern is simple: configure once, keep secrets safe, verify startup, then let the model use the tools.

Next: **Building Servers with SDKs**, where you learn how to write and test a complete MCP server from scratch.
