Compatibility
Legacy MCP prompt server
Deprecated prompt-serving host; maintained outbound MCP tools are a separate integration.
View Markdown source ↗Compatibility scope: this page describes the deprecated MCP host that exposes ChatMD prompts as agents. New session servers use the Ochat agent protocol. This deprecation does not apply to MCP-backed tools declared in ChatMD or their maintained client/type/transport libraries. Existing compatibility behavior below is retained; the new daemon does not require redesigning this server.
mcp_server launches an instance of the in-memory registry from
Mcp_server_core, registers a small set
of built-in tools and turns every *.chatmd prompt file in the prompts
folder into an agent-backed tool. The registry is then exposed either over
standard I/O or over HTTP, depending on command-line flags.
1 Synopsis
Section titled “1 Synopsis”$ mcp_server [--http PORT] [--require-auth] [--client-id ID] [--client-secret SECRET]When --http is not given, the program reads one JSON-RPC envelope per line
from stdin and writes the responses line-delimited to stdout. With
--http PORT it instead binds a small HTTP server on 127.0.0.1:PORT that
implements the transport described in Mcp_server_http.
The additional flags --require-auth, --client-id and --client-secret
are accepted for forward compatibility but are currently no-ops in the CLI
wrapper; authentication behaviour is described in the HTTP transport section
below.
2 Command-line flags
Section titled “2 Command-line flags”| Flag | Default | Description |
|---|---|---|
--http PORT | (absent) | Start the HTTP/SSE transport on PORT. |
--require-auth | false | Reserved for future use; currently ignored. The mcp_server binary always enforces OAuth 2.1 bearer tokens in HTTP mode. |
--client-id ID | (absent) | Reserved for future use; currently ignored. Dev OAuth client credentials come from MCP_DEV_CLIENT_ID. |
--client-secret SECRET | (absent) | Reserved for future use; currently ignored. Dev OAuth client credentials come from MCP_DEV_CLIENT_SECRET. |
3 Built-in tools
Section titled “3 Built-in tools”| Name | Purpose | Source |
|---|---|---|
echo | Returns the supplied text verbatim. | Internal demo helper |
apply_patch | Apply a textual V4A diff/patch to the repository. | Functions.apply_patch |
read_dir | List the contents of a directory. | Functions.read_dir |
get_contents | Read a file and return its contents. | Functions.get_contents |
webpage_to_markdown | Download a web page and convert it to Markdown. | Functions.webpage_to_markdown |
meta_refine | Refine a meta-prompt using LLM-backed heuristics. | Functions.meta_refine |
The MCP server registers its get_contents tool directly from the OCaml
library. Nested <read> roots in a ChatMD prompt configure that prompt’s
agent-side read_file tool; they do not alter this server-wide MCP tool.
Every prompt file discovered under the directory referenced by
$MCP_PROMPTS_DIR (or ./prompts when the variable is unset) is also
registered as two additional resources:
- a prompt that users can select via
prompts/*JSON-RPC calls; - a tool exposing the prompt via
tools/call.
Environment variables
Section titled “Environment variables”The mcp_server binary honours a small set of environment variables:
MCP_PROMPTS_DIR– Directory scanned for*.chatmdprompt files. If unset it defaults to./promptsrelative to the server’s current working directory.MCP_ADDITIONAL_ROOTS– Optional colon-separated list of additional roots that the server advertises fromroots/list. Each entry is turned into a root whoseuriis afile://...URI and whosenameis the basename.MCP_DEV_CLIENT_ID,MCP_DEV_CLIENT_SECRET– Credentials for the pre-registered development OAuth client accepted by the/tokenendpoint in HTTP mode. When unset they default todev-client/dev-secret.
4 Stdio transport
Section titled “4 Stdio transport”In stdio mode the program expects exactly one JSON value per line. Each
value is parsed and dispatched to Mcp_server_router.
All emitted JSON values are likewise terminated with a newline so that the
parent process can treat the stream as line-delimited JSON (LD-JSON).
stdin stdout│ │▼ ▲[ JSON ]–––▶ [ JSON ]The loop runs in the main Eio fibre and blocks indefinitely.
9 Internal helper functions
Section titled “9 Internal helper functions”The executable defines several helper functions that, while not exported
by any public library, are useful to understand when extending the
server. All of them reside in bin/mcp_server.ml and mutate the
in-memory registry passed as their first argument.
| Function | Purpose |
|---|---|
setup_tool_echo | Register the trivial echo tool that returns the supplied text verbatim. |
register_builtin_apply_patch | Expose the apply_patch tool backed by Functions.apply_patch. |
register_builtin_read_dir | Register the read_dir tool backed by Functions.read_dir. |
register_builtin_get_contents | Register the get_contents tool backed by Functions.get_contents. |
run_stdio | Launch the line-delimited JSON stdio transport when --http is absent. |
Below is a condensed API reference.
setup_tool_echo
Section titled “setup_tool_echo”val setup_tool_echo : Mcp_server_core.t -> unitRegisters the echo tool whose handler simply returns the text argument
unchanged.
register_builtin_apply_patch
Section titled “register_builtin_apply_patch”val register_builtin_apply_patch : Mcp_server_core.t -> dir:_ Eio.Path.t -> unitAdds the apply_patch tool that expects a single input field holding a
Unified-V4A patch. The return value is the patched text.
register_builtin_read_dir
Section titled “register_builtin_read_dir”val register_builtin_read_dir : Mcp_server_core.t -> dir:_ Eio.Path.t -> unitRegisters the read_dir tool. Arguments:
path– file-system path; may be relative to the server’s working directory.
The handler returns a JSON string containing newline-separated directory entries,
not a JSON array. Filesystem failures are also returned as text (prefixed with
error running read_directory:), rather than as structured handler errors.
register_builtin_get_contents
Section titled “register_builtin_get_contents”val register_builtin_get_contents : Mcp_server_core.t -> dir:_ Eio.Path.t -> unitRegisters the get_contents tool. Arguments:
file– path to the text file.
The entire file contents are returned as a JSON string.
run_stdio
Section titled “run_stdio”val run_stdio : core:Mcp_server_core.t -> env:Eio.Stdenv.t -> unitStarts the LD-JSON stdio transport. The function never returns. It is
only used when the --http flag is not supplied at start-up.
5 HTTP transport
Section titled “5 HTTP transport”When the --http flag is supplied the stdio loop is replaced by a call to
Mcp_server_http.run. The function
binds a lightweight Piaf server that supports:
- JSON-RPC 2.0 over
POST /mcpfor requests and batches; - Server-Sent Events for push notifications via
GET /mcp(tools, prompts and resources list-changed events, progress updates and structured logs); - OAuth2 helper endpoints under
/.well-known/oauth-authorization-server,/token,/authorizeand/register.
The wrapper selects one transport per process. It does not simultaneously serve stdio and HTTP clients or share a registry across separately launched processes.
Authentication and tokens
Section titled “Authentication and tokens”The standalone mcp_server binary always calls
Mcp_server_http.run ~require_auth:true, so the HTTP transport currently
always enforces OAuth 2.1 bearer tokens. There is no CLI flag to run the
HTTP endpoint without authentication.
By default the server exposes a single development client whose
credentials come from MCP_DEV_CLIENT_ID and MCP_DEV_CLIENT_SECRET,
falling back to dev-client / dev-secret when the variables are unset.
A minimal local flow looks like this:
-
Obtain a token:
Terminal window $ curl -X POST http://127.0.0.1:8080/token \-d 'grant_type=client_credentials' \-d "client_id=${MCP_DEV_CLIENT_ID:-dev-client}" \-d "client_secret=${MCP_DEV_CLIENT_SECRET:-dev-secret}"The response body contains an
access_tokenthat you can use in subsequent requests. -
Call the MCP endpoint using that token and a session id:
Terminal window $ curl -X POST http://127.0.0.1:8080/mcp \-H "Authorization: Bearer $ACCESS_TOKEN" \-H "Content-Type: application/json" \-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'The first successful
initializecall creates a new session identifier that is returned in theMcp-Session-Idresponse header. Subsequent requests must repeat the same header; see theMcp_server_httpdocumentation for full details.
Additional MCP methods and notifications
Section titled “Additional MCP methods and notifications”Beyond tools and prompts, mcp_server also exposes several other MCP
operations:
roots/list– reports the current working directory as a root and adds any extra roots fromMCP_ADDITIONAL_ROOTS.resources/list– enumerates regular files in the server’s current working directory.resources/read– reads file-based resources addressed byfile://URIs and refuses files larger than 1 MiB.
When using the HTTP transport the server emits Server-Sent Events on the
GET /mcp channel for:
notifications/tools/list_changednotifications/prompts/list_changednotifications/resources/list_changednotifications/progressnotifications/message(structured logging)
Stdio clients can invoke the same JSON-RPC methods but do not see these streaming notifications.
6 Hot-reloading of prompts and resources
Section titled “6 Hot-reloading of prompts and resources”Two background fibres poll the prompts directory and the current working directory every ten seconds:
- Prompt polling – newly added
*.chatmdfiles are parsed and registered without restarting the server. Deletions are not detected yet. - Resource polling – emits
resources/list_changednotifications when new regular files appear or disappear in the CWD.
Both fibres live under the same switch as the transport (HTTP or stdio), so they terminate automatically when the main service shuts down.
7 Exit codes
Section titled “7 Exit codes”| Code | Meaning |
|---|---|
0 | Clean shutdown (Ctrl-C or programmatic stop). |
≠0 | Unhandled exception – inspect stderr for the stack trace. |
8 Limitations & notes
Section titled “8 Limitations & notes”- Only one transport can be active at a time: specifying
--httpdisables the stdio loop. - The HTTP server binds to localhost exclusively. Use a reverse proxy if external access is required.
- File-watching relies on cheap polling; a future iteration will switch to platform-specific watchers when exposed by Eio.
- In the CLI wrapper the HTTP transport always requires a valid bearer token; an unauthenticated mode is not currently exposed.
- Stdio clients do not receive streaming notifications
(
notifications/*/list_changed,notifications/progress,notifications/message); those are HTTP-only. - In stdio mode a single malformed JSON line on stdin will currently terminate the process rather than returning a structured JSON-RPC error.
- The
resourcesAPI is intentionally minimal:resources/listonly covers regular files in the server’s current working directory,resources/readrefuses files larger than 1 MiB and URIs are simplefile://<filename>values. tools/listandresources/listalways return the full set; server-side filtering and pagination are not implemented.
© The documentation is released into the public domain. No warranties.