Skip to content
ochat
Search documentation

Use quotes for an exact phrase.

Search by topic, command, or code identifier.

    GitHub ↗

    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.


    Terminal window
    $ 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.

    FlagDefaultDescription
    --http PORT(absent)Start the HTTP/SSE transport on PORT.
    --require-authfalseReserved 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.
    NamePurposeSource
    echoReturns the supplied text verbatim.Internal demo helper
    apply_patchApply a textual V4A diff/patch to the repository.Functions.apply_patch
    read_dirList the contents of a directory.Functions.read_dir
    get_contentsRead a file and return its contents.Functions.get_contents
    webpage_to_markdownDownload a web page and convert it to Markdown.Functions.webpage_to_markdown
    meta_refineRefine 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:

    1. a prompt that users can select via prompts/* JSON-RPC calls;
    2. a tool exposing the prompt via tools/call.

    The mcp_server binary honours a small set of environment variables:

    • MCP_PROMPTS_DIR – Directory scanned for *.chatmd prompt files. If unset it defaults to ./prompts relative to the server’s current working directory.
    • MCP_ADDITIONAL_ROOTS – Optional colon-separated list of additional roots that the server advertises from roots/list. Each entry is turned into a root whose uri is a file://... URI and whose name is the basename.
    • MCP_DEV_CLIENT_ID, MCP_DEV_CLIENT_SECRET – Credentials for the pre-registered development OAuth client accepted by the /token endpoint in HTTP mode. When unset they default to dev-client / dev-secret.

    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.

    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.

    FunctionPurpose
    setup_tool_echoRegister the trivial echo tool that returns the supplied text verbatim.
    register_builtin_apply_patchExpose the apply_patch tool backed by Functions.apply_patch.
    register_builtin_read_dirRegister the read_dir tool backed by Functions.read_dir.
    register_builtin_get_contentsRegister the get_contents tool backed by Functions.get_contents.
    run_stdioLaunch the line-delimited JSON stdio transport when --http is absent.

    Below is a condensed API reference.

    val setup_tool_echo : Mcp_server_core.t -> unit

    Registers the echo tool whose handler simply returns the text argument unchanged.

    val register_builtin_apply_patch :
    Mcp_server_core.t -> dir:_ Eio.Path.t -> unit

    Adds the apply_patch tool that expects a single input field holding a Unified-V4A patch. The return value is the patched text.

    val register_builtin_read_dir :
    Mcp_server_core.t -> dir:_ Eio.Path.t -> unit

    Registers 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.

    val register_builtin_get_contents :
    Mcp_server_core.t -> dir:_ Eio.Path.t -> unit

    Registers the get_contents tool. Arguments:

    • file – path to the text file.

    The entire file contents are returned as a JSON string.

    val run_stdio : core:Mcp_server_core.t -> env:Eio.Stdenv.t -> unit

    Starts the LD-JSON stdio transport. The function never returns. It is only used when the --http flag is not supplied at start-up.

    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 /mcp for 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, /authorize and /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.

    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:

    1. 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_token that you can use in subsequent requests.

    2. 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 initialize call creates a new session identifier that is returned in the Mcp-Session-Id response header. Subsequent requests must repeat the same header; see the Mcp_server_http documentation for full details.

    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 from MCP_ADDITIONAL_ROOTS.
    • resources/list – enumerates regular files in the server’s current working directory.
    • resources/read – reads file-based resources addressed by file:// 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_changed
    • notifications/prompts/list_changed
    • notifications/resources/list_changed
    • notifications/progress
    • notifications/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 *.chatmd files are parsed and registered without restarting the server. Deletions are not detected yet.
    • Resource polling – emits resources/list_changed notifications 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.

    CodeMeaning
    0Clean shutdown (Ctrl-C or programmatic stop).
    ≠0Unhandled exception – inspect stderr for the stack trace.
    • Only one transport can be active at a time: specifying --http disables 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 resources API is intentionally minimal: resources/list only covers regular files in the server’s current working directory, resources/read refuses files larger than 1 MiB and URIs are simple file://<filename> values.
    • tools/list and resources/list always return the full set; server-side filtering and pagination are not implemented.

    © The documentation is released into the public domain. No warranties.