Repository navigation
feat(mcp): configurable server name and instructions - #512
Conversation
The MCP server identity was hardcoded as `docs-mcp-server` with a stale `0.1.0` version, and no `instructions` were sent during initialization. Users running several instances could not tell them apart, and had no way to tell the model what a given server indexes or how to use it. Add three settings to the `server` config section, each settable via config file, environment variable, or CLI flag: - `server.name` (`DOCS_MCP_SERVER_NAME`, `--server-name`) — reported in `serverInfo`; defaults to `docs-mcp-server`. - `server.instructions` (`DOCS_MCP_SERVER_INSTRUCTIONS`, `--server-instructions`) — sent as the `instructions` field. - `server.instructionsFile` (`DOCS_MCP_SERVER_INSTRUCTIONS_FILE`, `--server-instructions-file`) — read at startup as the instructions text; an unreadable file fails startup. Ignored when the inline value is set. Blank values are treated as absent so an empty env var falls back to the default or the file. The reported version now comes from the build-time package version instead of the hardcoded string. The CLI script name and the `envPaths` data directory still use the literal `docs-mcp-server`, so existing installs are unaffected. Closes arabold#511 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
arabold
left a comment
There was a problem hiding this comment.
Thank you for this pull request. The feature is useful, and it fits well into the existing server configuration section. The tests are good. I ran them locally with Node 22, and they pass.
Two things must change before we can merge. See the inline comments for details:
- The priority between
instructionsandinstructionsFiledoes not follow our configuration rules. - The instructions file is read on every HTTP request, not once at startup.
One more thing: we describe expected behavior in openspec/specs/. New configuration settings are listed in openspec/specs/configuration/spec.md (see "Server Public Origin Configuration" as an example). The contributor guide does not mention this yet, so this is not a problem with your work. I can add this update myself, or you can add it to this pull request if you want to.
| * @throws {Error} If the configured instructions file cannot be read. | ||
| */ | ||
| export function resolveServerInstructions(config: AppConfig): string | undefined { | ||
| if (config.server.instructions !== undefined) { |
There was a problem hiding this comment.
The inline value always wins over the file. It does not matter where each value comes from. This breaks our priority order:
command line flag > environment variable > configuration file
Example:
config.yamlcontainsserver.instructions: "A"- The user starts the server with
--server-instructions-file b.md - Result: the server sends "A". The command line flag is ignored, and the user sees no message.
Suggestion: stop at startup with a clear error when both instructions and instructionsFile are set. This is simple, and it matches how you already handle a file that cannot be read.
There was a problem hiding this comment.
Applied the suggestion in the latest commits, now it should follow correctly the priority order and the mutually exclusive suggestion.
| tools: {}, | ||
| resources: {}, | ||
| }, | ||
| instructions: resolveServerInstructions(config), |
There was a problem hiding this comment.
createMcpServerInstance() is called more than once. In HTTP mode, it is called for every new session on /sse and for every request on /mcp (see src/services/mcpService.ts). So the file is read from disk on every request.
Problems:
- If the file is deleted while the server runs, every request fails with HTTP status 500.
- If the file is changed, the new text is used right away. The documentation says the file is read once at startup.
Suggestion: read the file once when the server starts (in registerMcpService() and in startStdioServer()). Then pass the text to createMcpServerInstance() as a parameter.
| { | ||
| name: "docs-mcp-server", | ||
| version: "0.1.0", | ||
| name: config.server.name, |
There was a problem hiding this comment.
Question, not a blocker: the MCP specification has a separate title field for a display name that people read. name is meant as a technical identifier. Your goal is to tell instances apart in the client, so title may be the better field. The MCP library we use (@modelcontextprotocol/sdk) already supports title.
What do you think about setting title as well?
There was a problem hiding this comment.
I think that's perfectly fine, relevant change to keep together with this PR, I will add title as well!
| |:-------|:--------|:------------| | ||
| | `name` | `docs-mcp-server` | Server name reported to MCP clients during initialization. Set this to tell instances apart when running more than one. | | ||
| | `instructions` | - | Instructions text sent to MCP clients during initialization. Clients inject it into the model's context, so use it to describe what this server indexes and how to use it. | | ||
| | `instructionsFile` | - | Path to a file whose contents are used as `instructions`. Read once at startup; a missing or unreadable file fails startup. Ignored when `instructions` is set. | |
There was a problem hiding this comment.
Two notes:
- "Read once at startup" is not true in HTTP mode right now. See my comment in
src/mcp/mcpServer.ts. - A relative path is resolved from the current working directory. When a desktop app starts the server with stdio, this directory is often unknown. Please add a note that the path should be absolute.
| @@ -2,20 +2,27 @@ | |||
| * Tests for MCP server read-only mode functionality | |||
There was a problem hiding this comment.
Small thing: this file now also tests the server name and the instructions. Please update this comment.
Closes #511
Summary
The MCP server identity was hardcoded as
docs-mcp-serverwith a stale0.1.0version, and noinstructionswere sent during initialization. This adds three settings to theserverconfig section, each settable via config file, environment variable, or CLI flag on thedefaultandmcpcommands:server.nameDOCS_MCP_SERVER_NAME--server-namedocs-mcp-serverserver.instructionsDOCS_MCP_SERVER_INSTRUCTIONS--server-instructionsserver.instructionsFileDOCS_MCP_SERVER_INSTRUCTIONS_FILE--server-instructions-fileinstructionsis sent as the MCPinstructionsfield oninitialize. When unset, the field is omitted as before.instructionsFileis read once per server instance as UTF-8. An unreadable file fails startup with a clear error rather than silently sending nothing. The inline value takes precedence over the file.--flag "") are treated as absent, matching howpublicOriginbehaves.__APP_VERSION__instead of the hardcoded0.1.0.Out of scope
The CLI script name and the
envPathsdata directory still use the literaldocs-mcp-server, so existing installs and databases are unaffected.Tests
src/mcp/mcpServer.test.ts: connects a real client overInMemoryTransportand asserts the name, version, inline instructions, file instructions, inline-over-file precedence, missing-file error, and the no-instructions case.src/utils/config.test.ts: default, env, CLI precedence, and blank-value handling for the three settings.src/cli/commands/{mcp,default}.test.ts: the three flags parse under a strict parser and reachloadConfig.test/mcp-stdio-e2e.test.ts: spawns the server with the env vars set and asserts theinitializeresult.npm run lint,npm run typecheck, andnpm testpass locally (Node 22).