# How to Debug MCP Installation Failures When Merging with an Existing .mcp.json

> Fix MCP installation failures merging .mcp.json. Troubleshoot JSON errors, permissions, and overwrites using debug flags and validation tools for seamless integration.

- Repository: [Daniel Avila/claude-code-templates](https://github.com/davila7/claude-code-templates)
- Tags: how-to-guide
- Published: 2026-04-26

---

**MCP installation failures during merging typically stem from malformed JSON in either the remote template or local [`.mcp.json`](https://github.com/davila7/claude-code-templates/blob/main/.mcp.json), file permission errors, or silent key overwrites in the `mcpServers` object.** Run the command with `DEBUG=cli*` to expose the exact merge step and file paths, validate both JSON files with `jq`, and check for duplicate server names that cause silent overwrites.

The `claude-code-templates` CLI automates MCP server setup by downloading remote [`.mcp.json`](https://github.com/davila7/claude-code-templates/blob/main/.mcp.json) configurations and merging them into your existing project files. When the merge logic in [`cli-tool/src/index.js`](https://github.com/davila7/claude-code-templates/blob/main/cli-tool/src/index.js) encounters invalid syntax, permission conflicts, or duplicate server keys, the installation can fail silently or throw cryptic parsing errors. This guide walks through the exact debugging workflow using the source code from the davila7/claude-code-templates repository.

## Understanding the MCP Merge Process

When you run `npx claude-code-templates … --mcp <name>`, the CLI executes a specific sequence in [`cli-tool/src/index.js`](https://github.com/davila7/claude-code-templates/blob/main/cli-tool/src/index.js) (lines 682‑735):

1. **Download** the remote [`.mcp.json`](https://github.com/davila7/claude-code-templates/blob/main/.mcp.json) from a raw GitHub URL.
2. **Parse** the JSON into `mcpConfig`.
3. **Strip descriptions** to persist only essential command/args/env (lines 684‑89):

```js
// cli-tool/src/index.js – lines 684‑89
if (mcpConfig.mcpServers) {
  for (const serverName in mcpConfig.mcpServers) {
    delete mcpConfig.mcpServers[serverName].description;
  }
}

```

4. **Detect** an existing local [`.mcp.json`](https://github.com/davila7/claude-code-templates/blob/main/.mcp.json) (`targetMcpFile`).
5. **Merge** configurations using shallow spread for top‑level properties and object spread for `mcpServers` (lines 692‑713):

```js
// cli-tool/src/index.js – lines 692‑713
const mergedConfig = { ...existingConfig, ...mcpConfig };
if (existingConfig.mcpServers && mcpConfig.mcpServers) {
  mergedConfig.mcpServers = {
    ...existingConfig.mcpServers,
    ...mcpConfig.mcpServers
  };
}

```

6. **Write** the merged JSON with 2‑space indentation:

```js
await fs.writeJson(targetMcpFile, mergedConfig, { spaces: 2 });

```

7. **Report** success/failure via `trackingService.trackInstallationOutcome`.

Errors are caught in a single `catch` block that logs the message and records a failure outcome.

## Common Failure Symptoms and Root Causes

| Symptom | Typical Cause |
|---------|---------------|
| **Error installing MCP: Unexpected token …** | Invalid JSON syntax (trailing commas or comments) in the downloaded file or local [`.mcp.json`](https://github.com/davila7/claude-code-templates/blob/main/.mcp.json). |
| **Error installing MCP: ENOENT: no such file or directory** | The target directory is missing, or the CLI lacks write permissions. |
| **Existing .mcp.json found, merging configurations…** followed by a silent failure | Conflict in merge logic (duplicate server keys) or a runtime exception while writing the file. |
| **MCP installed successfully but the new server is missing** | The deep‑merge step skipped the entry because `mcpServers` was undefined or the key was silently overwritten. |

## Step-by-Step Debugging Checklist

### Validate Remote and Local JSON

Corrupt JSON is the most common failure point. Verify the remote template first by opening the raw URL directly, then validate your local file:

```bash

# Validate local JSON syntax

node -e "JSON.parse(require('fs').readFileSync('.mcp.json','utf8'))"

```

If this throws, the parser in [`cli-tool/src/mcp-stats.js`](https://github.com/davila7/claude-code-templates/blob/main/cli-tool/src/mcp-stats.js) (line 19) will also fail.

### Check File Permissions and Paths

The `targetMcpFile` path is built with `path.join(targetDir, '.mcp.json')`. If you mistakenly pass `--global` or an incorrect `--target`, the file may be written elsewhere or fail with `EACCES`.

```bash

# Verify target location

ls -l .mcp.json

# Or temporarily patch the CLI to log the path

console.log('🔧 Target:', targetMcpFile)

```

### Inspect for Server Key Collisions

The merge uses object spread (`...`), which **silently overwrites** duplicate keys. Compare server names before merging:

```bash
node - <<'EOF'
const fs = require('fs');
const local = JSON.parse(fs.readFileSync('.mcp.json','utf8'));
console.log('Local server keys:', Object.keys(local.mcpServers || {}));
EOF

```

If a remote server shares a key with an existing entry, the new configuration replaces the old without warning.

### Verify the mcpServers Property Exists

An empty result often indicates the remote file lacks the top‑level `mcpServers` property. Ensure the source file follows the canonical format:

```json
{
  "mcpServers": {
    "server-name": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-name"]
    }
  }
}

```

### Disable Telemetry to Isolate Network Errors

The `trackingService` call inside the `try` block can throw network errors that mask file-system issues. Set `DISABLE_TRACKING=1` to bypass telemetry during debugging:

```bash
DISABLE_TRACKING=1 DEBUG=cli* npx claude-code-templates --mcp my-mcp-name

```

## Practical Debug Commands

Run the installation with maximum verbosity to see exact URLs, file paths, and the intermediate merged object:

```bash
DEBUG=cli* npx claude-code-templates --mcp my-mcp-name

```

Fetch and inspect the remote configuration manually:

```bash
node - <<'EOF'
const fetch = require('node-fetch');
(async () => {
  const url = 'https://raw.githubusercontent.com/davila7/claude-code-templates/main/cli-tool/components/mcps/my-mcp-name/.mcp.json';
  const txt = await (await fetch(url)).text();
  console.log('Remote .mcp.json →', JSON.stringify(JSON.parse(txt), null, 2));
})();
EOF

```

Compare local versus remote server keys to spot collisions:

```bash
node - <<'EOF'
const fs = require('fs');
const local = JSON.parse(fs.readFileSync('.mcp.json','utf8'));
const remote = JSON.parse(fs.readFileSync('tmp-remote.json','utf8'));
console.log('Local keys :', Object.keys(local.mcpServers||{}));
console.log('Remote keys:', Object.keys(remote.mcpServers||{}));
EOF

```

Inspect the final merged file with `jq`:

```bash
cat .mcp.json | jq '.mcpServers'

```

## Resolving Common Merge Issues

| Issue | Why it happens | Fix |
|-------|----------------|-----|
| **Unexpected token** on merge | Remote file contains comments or trailing commas. | Clean the file or use a JSON‑with‑comments parser. |
| **Duplicate server names silently overwritten** | Object spread overwrites by key. | Rename either server before merging, or manually edit [`.mcp.json`](https://github.com/davila7/claude-code-templates/blob/main/.mcp.json) post-install. |
| **Empty `mcpServers` in result** | Remote file missed the top‑level `mcpServers` property. | Ensure the source uses `{ "mcpServers": { … } }` structure. |
| **Permissions denied (`EACCES`)** | Running CLI in a system directory without write access. | Use a user‑writable target directory or `chown` the project folder. |
| **Telemetry failure masks real error** | Network error in `trackingService` propagates to outer catch. | Set `DISABLE_TRACKING=1` while troubleshooting. |

## Summary

- **Debug MCP installation failures** by running `DEBUG=cli*` to expose the merge logic in [`cli-tool/src/index.js`](https://github.com/davila7/claude-code-templates/blob/main/cli-tool/src/index.js) (lines 682‑735).
- **Validate JSON syntax** on both remote templates and local [`.mcp.json`](https://github.com/davila7/claude-code-templates/blob/main/.mcp.json) files before merging.
- **Check for key collisions** in `mcpServers`, as the object spread implementation silently overwrites duplicates.
- **Verify file permissions** on the target directory to prevent `ENOENT` and `EACCES` errors.
- **Disable telemetry** with `DISABLE_TRACKING=1` to prevent network errors from obscuring file-system issues.

## Frequently Asked Questions

### Why is my new MCP server missing after a "successful" installation?

This occurs when the **deep‑merge step skips the entry**, typically because the remote file lacked a properly defined `mcpServers` object or the server key was silently overwritten by an existing entry with the same name. Inspect the final [`.mcp.json`](https://github.com/davila7/claude-code-templates/blob/main/.mcp.json) with `jq '.mcpServers'` to confirm the new server appears under a unique key.

### How do I fix "Unexpected token" errors during the merge?

This error indicates **invalid JSON syntax**—often trailing commas or comments—in either the downloaded remote file or your local [`.mcp.json`](https://github.com/davila7/claude-code-templates/blob/main/.mcp.json). Validate both files using `node -e "JSON.parse(require('fs').readFileSync('.mcp.json'))"` or an online linter. The parser at line 19 of [`cli-tool/src/mcp-stats.js`](https://github.com/davila7/claude-code-templates/blob/main/cli-tool/src/mcp-stats.js) strictly requires standard JSON.

### Why did my existing server configuration get overwritten?

The merge implementation uses **object spread syntax** (`...existingConfig.mcpServers, ...mcpConfig.mcpServers`), which overwrites properties with identical keys. If the remote server name matches an existing one, the new configuration replaces the old. Rename one of the servers before installation to preserve both configurations.

### How can I disable telemetry while debugging MCP installation issues?

Set the environment variable `DISABLE_TRACKING=1` before running the command. The `trackingService` in [`cli-tool/src/tracking-service.js`](https://github.com/davila7/claude-code-templates/blob/main/cli-tool/src/tracking-service.js) checks this variable; disabling it prevents network errors from being caught in the main `try` block and reported as installation failures, allowing you to see the actual file-system or parsing error.