MCP servers

MCP (Model Context Protocol) is a shared format that lets AI assistants talk to other systems. An MCP server is a small program or web service that offers tools, data and prompts. Connect one, and Syntic Code can query your database, read tickets from your tracker, search your internal docs or call your own APIs, with the same permission checks as its built-in tools.

New to MCP? Start with the MCP quickstart. This page is the full reference.

What a server adds

A server offersShows up in Syntic Code as
ToolsTools named mcp__<server>__<tool>, which the agent calls like Read or Bash.
Resources (files, records, documents)Things you attach to a message with @<server>:<uri>, or the agent reads with ReadMcpResource.
Prompts (saved prompt templates)Slash commands: /mcp__<server>__<prompt>.
Questions for you (elicitation)A form in the session asking you to fill in or confirm something.
Pushed messages (channels)New turns started by the server, when you allow it (Channels).

Add a server

syntic mcp add [options] <name> <command-or-url> [args...]

Local program (stdio)

Syntic Code starts the program and talks to it over standard input and output.

syntic mcp add files -- npx -y @example/files-server ~/notes
syntic mcp add db -e DATABASE_URL=postgres://localhost/dev -- npx -y @example/postgres-mcp

Everything after the command is passed to it unchanged, flags included. Use -- before the command when it starts with a flag of its own, so Syntic Code does not read it as one of its options.

Remote server over HTTP

syntic mcp add tracker https://mcp.tracker.example/mcp
syntic mcp add tracker https://mcp.tracker.example/mcp --header "Authorization: Bearer $TRACKER_TOKEN"

Remote server over server-sent events

syntic mcp add events https://events.example/sse --transport sse

WebSocket

A ws:// or wss:// URL in the config file uses WebSocket. Write it in the file with "type": "ws" or just the URL.

How the transport is chosen

Without --transport, an argument that looks like a URL is treated as a remote server: one whose path ends in /sse uses SSE, any other uses HTTP. Anything else is a program to run. --transport stdio|sse|http overrides the guess.

Options for syntic mcp add

OptionMeaning
-t, --transport <stdio|sse|http>Pick the transport instead of guessing.
-e, --env KEY=VALUEAn environment variable for a local server. Repeat for more.
--header "Name: Value"A request header for a remote server. Repeat for more.
-s, --scope <local|project|user>Where to save it (below). Default local.

From JSON

When a server’s setup notes give you a JSON object, add it as is:

syntic mcp add-json files '{"command":"npx","args":["-y","@example/files-server"]}'
syntic mcp add-json tracker '{"type":"http","url":"https://mcp.tracker.example/mcp"}' --scope user

Scopes

Every server lives in one scope. Each scope is a separate file:

--scopeFileWho gets it
local (default).syntic/settings.local.json in the current folderYou, in this project. Syntic Code adds the file to .syntic/.gitignore, so tokens passed with -e or --header are not committed.
project.mcp.json at the project rootEveryone who has the repository. Commit it.
user~/.syntic/.mcp.jsonYou, in every project.

Servers in .syntic/.mcp.json (where older versions saved project servers) are still loaded and can be listed and removed, but new servers are never written there.

Servers can also come from:

  • mcpServers in any settings file;
  • an enabled plugin;
  • --mcp-config <file or JSON> for one run. Add --strict-mcp-config to use only those and ignore everything else.

On a name clash, a --mcp-config server wins. The same server set up twice under different names (same command and environment, or same URL) is connected only once. A plugin server that copies one you set up yourself is skipped.

Manage servers

CommandDoes
syntic mcp list [--scope …] [--json] [--check]List servers with their scope, transport and target. --check starts each local server once to see whether it answers and how many tools it offers (project servers only if you approved them).
syntic mcp get <name> [--scope …] [--json]Show one server, with secrets masked.
syntic mcp remove <name> [--scope …]Remove it. If it is in several scopes, the command lists them and asks you to pick one.
syntic mcp reset-project-choicesForget your approve and reject answers for this project’s servers.
syntic mcp login <name> [--no-browser]Sign in to a remote server that uses OAuth.
syntic mcp logout <name>Forget that sign-in.
syntic mcp serveRun Syntic Code itself as an MCP server (below).

These work without starting a session, so a setup script or CI job can register servers.

The config file

Every scope uses the same shape:

{
  "mcpServers": {
    "files": {
      "command": "npx",
      "args": ["-y", "@example/files-server", "${HOME}/notes"],
      "env": { "API_KEY": "${FILES_API_KEY}" },
      "request_timeout_ms": 120000
    },
    "tracker": {
      "type": "http",
      "url": "https://mcp.tracker.example/mcp",
      "headers": { "Authorization": "Bearer ${TRACKER_TOKEN}" }
    },
    "wiki": {
      "type": "http",
      "url": "https://wiki.internal.example/mcp",
      "headersHelper": "~/bin/wiki-token.sh",
      "alwaysLoad": true
    }
  }
}
FieldForMeaning
command, argsLocalThe program and its arguments.
envLocalExtra environment variables for the program.
typeAllstdio, http, sse or ws. Usually inferred from the other fields.
urlRemoteThe server’s address.
headersRemoteExtra request headers.
headersHelperRemoteA shell command that prints a JSON object of headers, for tokens that expire (below).
oauthRemoteA pre-registered sign-in client: clientId, clientSecret, callbackPort (below).
request_timeout_ms (also requestTimeoutMs or timeout)AllHow long one tool call to this server may run, in milliseconds. Values under 1,000 are ignored.
alwaysLoadAllAlways send this server’s tool descriptions in full, never hold them back for tool search.

Unknown fields are ignored, and one broken entry is skipped with a message while the others still load.

Environment variables in the file

Values in command, args, env, url, headers and oauth.clientSecret may refer to environment variables:

WriteBecomes
${VAR}The value of VAR.
${VAR:-fallback}The value of VAR, or fallback when it is not set.

This keeps tokens out of shared files: commit "Bearer ${TRACKER_TOKEN}" and let each person set the variable. A server whose variables are missing is still started, and you are told which ones were not set.

Project servers ask first

A server defined by the repository (in .mcp.json, .syntic/.mcp.json or project settings) can run any program, so:

  • In a folder you have not trusted, project servers are not loaded at all.
  • In a trusted folder, the first interactive start shows each new server with the command it runs and asks Allow it to start? [y/N]. Your answer is remembered for the project in ~/.syntic/mcp_approvals.json.
  • If the repository later changes the server’s command, URL, arguments, environment, headers or headers helper, you are asked again.
  • In a -p run in a trusted folder, project servers are approved without asking.

Sign in to remote servers

OAuth

Servers that use OAuth sign in through your browser:

syntic mcp login tracker              # opens the browser
syntic mcp login tracker --no-browser # prints the address to open yourself
syntic mcp logout tracker

Or select the server in /mcp and press Enter. Syntic Code finds the server’s sign-in service, registers itself when the service allows that, and waits for the browser to come back to a local address. If the browser runs on another machine (over SSH, for example), copy the address of the page it ends on and paste it into the terminal.

Tokens are kept in your system’s credential store and refreshed before they expire. If a server rejects a token, the server shows as needs sign-in instead of failing.

A pre-registered client

Some services only accept sign-in clients an administrator registered by hand, with a fixed return address. Give Syntic Code that client:

{
  "mcpServers": {
    "corp-docs": {
      "type": "http",
      "url": "https://docs.corp.example/mcp",
      "oauth": {
        "clientId": "syntic-cli",
        "clientSecret": "${CORP_DOCS_CLIENT_SECRET}",
        "callbackPort": 7419
      }
    }
  }
}

Register http://127.0.0.1:<callbackPort>/callback as the return address with the service. SYNTIC_MCP_OAUTH_CALLBACK_PORT sets a fixed port for every sign-in.

Headers from a script

For tokens your company issues some other way, point headersHelper at a command that prints the headers as a JSON object:

#!/bin/sh
# ~/bin/wiki-token.sh
printf '{"Authorization": "Bearer %s"}' "$(corp-auth token --audience wiki)"

The helper runs when the server connects, and once more if the server answers 401 or 403. It gets SYNTIC_MCP_SERVER_NAME and SYNTIC_MCP_SERVER_URL, so one script can serve several servers. Its headers win over headers. A helper that fails, takes too long or prints anything but a flat object of strings adds no headers, and the reason is logged.

A server that already sends its own Authorization header does not use OAuth sign-in.

Use servers in a session

/mcp lists every server and its state: connected (with its tool count), off, not connected (with the reason), or needs sign-in.

Key in /mcpDoes
Enter, rReconnect (restarts a local server). On a server that needs sign-in, sign in.
Space, d, eTurn the server off for this session, or back on.
RReconnect every server that is down.
aSign in again to a remote server.

/mcp reconnect all does the same as R.

Servers start side by side when a session opens. A slow one does not hold up the start: it joins when it is ready. Each server’s tool list is also remembered between sessions, so its tools are available at once and a call made before the server is up waits for it. SYNTIC_MCP_TOOL_CACHE=0 turns that memory off.

Permissions

MCP tool calls go through the normal permission rules. Rules can name one tool or a whole server:

{
  "permissions": {
    "allow": ["mcp__tracker__search_issues", "mcp__docs__*"],
    "deny": ["mcp__db__run_sql"]
  }
}

Tools for reaching servers

Besides each server’s own tools, the agent has three helpers:

ToolDoes
ListMcpResourcesLists the resources servers offer, from one server or all.
ReadMcpResourceReads one resource by server and URI.
McpCalls a tool on a connected server by server name, tool name and arguments, for tools that are not offered under their own name.

Resources

Type @ and the server name to attach a resource to your message, for example @docs:file:///guide.md. The resource is read when the turn starts and its text is attached (with a size limit); your message keeps the short mention. Only names of connected servers count, so a normal @path is never mistaken for one.

Prompts

A server’s prompts appear in the / menu marked (MCP), as /mcp__<server>__<prompt>. Arguments typed after the command are passed to the prompt.

Questions from a server

A server can ask you for input in the middle of a tool call, for example to confirm an action or pick an account. A form opens in the session. If you do not answer within 10 minutes, the request is dismissed. Elicitation and ElicitationResult hooks can answer for you or change the answer.

Folders the server may use

Servers that ask which folders they may work in are told the session’s working folder first, then every folder added with --add-dir or /add-dir. When you add a folder during the session, those servers are told the list changed.

Many tools load on demand

When connected servers add many tools, their full descriptions are not sent with every request. The model sees a short list and loads what it needs with the ToolSearch tool. ENABLE_TOOL_SEARCH controls this: auto (the default, which defers once tool descriptions would take 10% of the context window), auto:N for another percentage, always or never. A server with "alwaysLoad": true is always sent in full. See The context window.

Limits and timeouts

WhatDefaultChange with
Time a server gets to start and list its tools30 sSYNTIC_MCP_TIMEOUT (milliseconds)
Time for each connection and handshake request30 sMCP_TIMEOUT (milliseconds)
Time one tool call may runNo practical limitMCP_TOOL_TIMEOUT (milliseconds), or request_timeout_ms per server
Time a call to a remote server may go without any reply5 minutesSYNTIC_MCP_TOOL_IDLE_TIMEOUT (milliseconds; 0 turns it off)
A tool call that runs longer than this moves to the background2 minutesSYNTIC_MCP_BACKGROUND_AFTER_SECS (0 never moves calls)
Size of one tool result25,000 tokensMAX_MCP_OUTPUT_TOKENS
Length of one tool description2,048 charactersSYNTIC_MCP_MAX_DESCRIPTION_CHARS

A tool result over the size limit is cut to fit, with a note telling the model it was shortened. Images in results count toward the limit too.

A call moved to the background keeps running. The turn gets a short note, the call appears under “Other tasks” in the agents view, and its real result reaches the agent when it ends. Press Ctrl+B to move a running call yourself. -p runs always wait for calls to finish.

Faster starts in scripts

By default a -p run waits for its MCP servers (up to the startup timeout) before the first request. Set MCP_CONNECTION_NONBLOCKING=true to give servers only a short window instead (5 seconds, or MCP_CONNECT_TIMEOUT_MS milliseconds; 0 means no MCP tools for the run). Servers not ready by then are left out of that run.

Run Syntic Code as an MCP server

syntic mcp serve

This offers Syntic Code’s built-in tools (Read, Edit, Bash, Grep and the rest) to another MCP client over standard input and output. Register it in the other program as a local server whose command is syntic mcp serve.

⚠️

Tool calls through syntic mcp serve run without permission prompts. Only connect clients you trust, and only in folders you are happy for them to change.

Settings, policy and switches

Setting / variableEffect
deniedMcpServersServers that never start (by name, command or URL pattern).
allowedMcpServersOrganization policy only: when set, only matching servers start. See Managed MCP.
SYNTIC_DISABLE_MCP=1Start no MCP servers.
--mcp-config <file or JSON>Add servers for one run.
--strict-mcp-configUse only the --mcp-config servers.
--bareOnly --mcp-config servers, nothing from settings or plugins.
--mcp-debugDebug logging for MCP only.

Troubleshooting

ProblemWhat to do
A server shows “not connected”Read the reason in /mcp. Run its command yourself in a terminal to see errors. --mcp-debug logs the connection to ~/.syntic/logs/syntic.log.
syntic mcp list --check times outThe server takes longer than 30 s to start (a package download, for example). Raise SYNTIC_MCP_TIMEOUT, or run the command once by hand to warm its cache.
A project server never appearsTrust the folder, then approve the server. If you declined earlier, run syntic mcp reset-project-choices.
”needs sign-in” keeps coming backsyntic mcp logout <name>, then syntic mcp login <name>. For services with a fixed client, check the oauth block and the registered return address.
A variable in the file is not replacedThe variable is not set in the environment Syntic Code started from. Use ${VAR:-fallback}, or set it before starting.
A tool’s output is cut offRaise MAX_MCP_OUTPUT_TOKENS, or ask the server for less (a filter, a page).
Long tool calls stopSet request_timeout_ms on the server, or MCP_TOOL_TIMEOUT. For remote servers that stay silent while working, raise SYNTIC_MCP_TOOL_IDLE_TIMEOUT.
Two copies of a serverThe same server is configured in two places under one name in different scopes. syntic mcp list shows both; remove one with --scope.

More checks are on Debug your configuration.