Repository navigation
Releases
Releases · xyTom/coding-tools-mcp
v0.5.0
0.5.0 - 2026-09-14
The v0.5.0 reliability work. Migration notes:
docs/migration-0.5.md; rationale:
docs/plan-v0.5.md.
Breaking
request_permissionsis advertised only indangerousmode. Outside it
the tool can only answerELICITATION_UNSUPPORTED, which is where roughly
60% of its observed calls went. It stays callable when hidden, so a direct
call still gets that same answer rather thanUnknown tool. The registry
holds 19 tools;safeandtrustedadvertise 18 anddangerousadvertises
19. A client that hardcodes the catalog must readtools/list.- The
write_generated_or_ignoredpermission kind is removed from the
request_permissionsschema. It was an enum value no code path requested or
granted. read_filemodel text now opens with a banner —
[Showing lines 1-40 of 40 revision=…]— on every read, not only a
truncated one.apply_changesneeds that revision and most clients forward
only the text.structuredContent.contentis unchanged.exec_command's defaulttimeout_msis now 300000 (was 30000).
timeout_msalways meant total process lifetime, but the old default was
shorter than an install or a build, so a command backgrounded by the yield
was killed shortly after the call returned.yield_time_msstill defaults to
10000 and the 600000 maximum is unchanged.
Added
apply_changes, a line-addressed editing tool. Each change names an
action (create,write,edit,delete,move,copy) and a path.
Existing targets use therevisionread_filereported.writeis an
upsert and may omit it when creating a missing path;createasserts absence.
Nothing has to match textually, and a file that changed since it was read is
refused withREVISION_MISMATCHinstead of being overwritten. A path may
appear once per call.read_filereports arevision, a SHA-256 of the whole file computed
during the pass that already streams it, so no second read can disagree with
the bytes the model saw.--workspace-mutation=structured-onlyand repeatable--write-path,
off by default. Structured-only makes the workspace read-only for
exec_commandunder Landlock, leavingapply_patchandapply_changesas
the only way in. Experimental: it breaks any command that writes into the
tree unless that directory is allowlisted. Also settable as
CODING_TOOLS_MCP_WORKSPACE_MUTATIONandCODING_TOOLS_MCP_WRITE_PATHS;
the effective policy and whether it is enforced appear inserver_infoas
workspace_mutation_policy. Full enforcement requires Landlock ABI 3 or
newer, and missing in-workspace write directories are created before rules
are installed.idempotency_keyonapply_patchandapply_changes. Replaying a key
with the same arguments returns the recorded result instead of doing the work
twice, so a lost response is safe to retry. A key names one request: it is
recorded with a fingerprint of the arguments that earned it, reusing it for
different arguments isIDEMPOTENCY_KEY_REUSEDrather than a replay of work
that was never done, and adry_runresult is never recorded at all. The
runtime keeps one 64-entry LRU of(tool, key)results across both tools,
not a separate cache per tool.- A repeat-failure circuit breaker. The third byte-identical call that
would produce the same deterministic error is refused with
REPEATED_CALL_BLOCKED. Changing any argument gives the revised call a fresh
budget; a successful write, including a move whose hunks were already
present, clears the breaker entirely. The first terminal observation of each
command_idfromexec_command,write_stdin,read_output, or
kill_commandalso clears it when that command could write: in unrestricted
mode, under an unenforced structured-only policy, through a configured write
path, or after Landlock setup failed open. Later observations of the same
command do not clear it again.IDEMPOTENCY_KEY_REUSEDis excluded because
its repair is a new key. - Per-tool
outputSchemaintools/list, replacing one generic envelope. - A real-task evaluation harness under
benchmarks/agent_eval/, which runs
the same tasks and prompt through an agent's native tools and through this
server and scores both on pass rate, first-attempt success, rounds to green,
regressions, and wall time. See
docs/agent-evaluation.md. Its gates are
release-announcement criteria, not merge criteria.
Changed
apply_patchlocates hunks with forward text anchors.@@ <context>
advances a language-agnostic search cursor; it does not infer function or
block boundaries.*** End of Filealso participates in placement.- Patch matching is graded — exact, then ignoring trailing whitespace, then
ignoring indentation width — and the grade actually used is reported in
match_quality, so a downgrade is visible rather than silent. - A successful patch returns evidence:
changed_ranges, a per-file
revision, andtotal_lines. These are evidence only;apply_patchstill
takes norevisionargument, because its context lines are already its
optimistic check. - A failed patch returns repair data: the hunk index, nearby numbered text,
and candidate match positions, so the next attempt can be aimed. - A patch whose changes are already present reports
already_applied
instead of failing, when the result is locatable: an exact or
trailing-whitespace match of a block that carries a context line, or a
multi-line addition. A context-free single line that happens to occur
somewhere in the file is a coincidence, not a completed edit, and still
fails withPATCH_CONTEXT_NOT_FOUND. A*** Move to:that actually
relocates the file remains a write and reportsalready_applied: falseeven
when every hunk was already present. apply_patchnow follows Codex primary-path and overwrite semantics. An
operation's primary path may appear only once in an envelope, including
aliases such asa.txtand./a.txt.Add Filemay replace an existing
file,Move tomay replace an existing destination, and distinct source
files may move to the same destination in order, with the later write
winning.apply_changescompares paths after resolving them, soa.txtand
./a.txtare one path: naming both isINVALID_ARGUMENTrather than a
silent overwrite reported as two applied changes.git_diffincludes untracked files by default, so a file created by
apply_patchis visible. Passinclude_untracked: falsefor the old
behavior.- Telemetry counts operations truthfully. A command that exits nonzero,
times out, or dies on a signal is no longer recorded as a successful tool
call; its terminal outcome is counted once rather than again on every
exec_command,write_stdin,read_output, orkill_commandobservation;
and consecutive failures are tracked per (tool, error code) rather than in
one global slot any tool's success could reset. A 0.5.0 dashboard is not
comparable to an earlier one. check_exec_environmentwarns on non-Linux hosts that there is no
Landlock and therefore no filesystem confinement.server_infodiscloses the output retention TTL and the completed-command
cap, which docs/limitations.md points callers at.- Project instructions now tell a model to prefer
apply_changesand
apply_patchoverexec_commandfor file edits. - The runtime contract now states that
patch_lockserializes patches within
one server process only; two servers on one workspace are protected by the
pre-commit baseline recheck alone.
Fixed
- Chained patch operations retain staged file state. A newly moved
destination can be updated, deleted, or moved again in the same envelope,
and a laterAdd Fileoverwrite preserves its staged executable mode.
Repeated destination writes keep the first baseline, so an intervening
external edit raisesPATCH_CONFLICTinstead of being overwritten. apply_changesno longer doubles carriage returns in CRLF replacement
content. Replacement text now normalizes LF, CRLF, and CR separators before
the file's original line-ending convention is restored, so returned
total_linesandchanged_rangesstay consistent with a subsequent
read_file.@@ <context>now follows Codex-style forward-cursor semantics. Missing
anchors fail instead of being ignored, matching never jumps back before the
anchor/current cursor, top-level and brace-based code are not rejected by
indentation heuristics, and pure-addition hunks validate their anchor before
appending at EOF.- Move evidence and breaker invalidation now follow actual staged
mutations. Moves that change paths retain an explicit source deletion in
affected_files, and successful workspace-mutation invalidation is derived
from committed staged actions rather than compressed display evidence. - Move mode preservation now survives later content reversion. A staged
file is considered unchanged only when both its content and mode match the
original destination baseline, so an executable source moved over a
non-executable destination keeps its executable bit even if later patch
operations restore the destination's original bytes. - Whole-file
apply_changesevidence now reports line counts consistently.
Rewriting identical content reports zero additions/removals, and replacement
ranges count removed lines from the original file. Bare-CR content is also
counted with the same universal-newline rules asread_filewithout
rewriting the user's bytes. - The long-running PTY compliance test now polls the bounded terminal stream
for final child output instead of assuming input echo and process output
arrive in one respons...
v0.3.0rc1
0.3.0rc1 - 2026-08-13
Changed
- Breaking: command handles are now named
command_id;kill_sessionis
nowkill_command; retained output references use
command:<command_id>:stdout|stderr. The former commandsession_id,
kill_session, andsession:output references are not accepted. - Commands and retained output are now owned by the workspace server rather
than by an individualMcp-Session-Id. Multiple authenticated clients for
the same workspace can continue, read, or terminate a command after
reconnecting or creating a new MCP transport session using thecommand_id
returned byexec_command. - Closing or expiring an HTTP transport session no longer terminates workspace
commands. Commands are still bounded by the existing active-count, retained
output, byte, timeout, and TTL limits and are terminated when the workspace
server shuts down. - Breaking:
notifications/cancelledno longer terminates the command the
cancelled request started. The notification is still accepted and, as
before, answered with no response. A command outlives the request that
started it and is shared by every client of the workspace, and the mapping
was keyed by the client's own JSON-RPC id, so two clients that both used
id: 1could cancel each other's commands. Terminate a command with
kill_command; the reduced cancellation responsiveness is tracked in issue
#48. - Breaking:
get_default_cwdandset_default_cwdare removed and the
default catalog is now 18 tools. A relativepathalways resolves against
the workspace root, so there is no session-scoped working directory to set,
read, or lose on reconnect. Pass a workspace-relativepath, or
exec_command'sworkdir, to target a subdirectory. Theread_file
next_actioncontinuation now repeats the workspace-relative path it was
given rather than one relative to a session cwd, andserver_infono longer
reportsdefault_cwd. - Tool descriptions now direct remote clients to pass explicit
path/workdir
arguments and include concrete examples for patching and command
continuation. - Error text now names the error's category and whether it is retryable, and
tells a model not to repeat a call that cannot succeed.retryableand
categorywere only ever instructuredContent, which most clients do not
forward to the model, so a permanent failure was indistinguishable from a
transient one. COMMAND_NOT_FOUNDfromwrite_stdin,kill_command, andread_outputnow
explains that the handle expired or never existed, states the retention
window a finished command's output has, and namesexec_commandas the way
to recover. Retrying a dead handle is the single largest source of failed
write_stdincalls.kill_commandnow declareskill_wait_ms(hard-kill escalation wait,
default 2000 ms) in its input schema; previously the runtime honored it but
schema validation rejected any call that passed it.read_outputno longer accepts the undocumentedcommand:<id>:full
reference form, which silently read stdout only. Use the per-stream
command:<id>:stdout/command:<id>:stderrreferences.- Retained command output now keeps the earliest bytes per stream (a frozen
head segment, one eighth of the per-stream budget) in addition to the
rolling tail, so the command echo and first errors survive large outputs.
read_outputreportshead_retained_bytesandevicted_gap_bytes. server_infoexposes anoutput_retentionblock naming the per-stream
retention budget (buffer_bytes_per_stream,head_bytes_per_stream). How
often that budget was actually hit is a runtime-wide measurement rather than
an answer to one client, so the eviction counters (evict_events,
evicted_bytes_total) and omitted-read counters
(read_output_omitted_hits,poll_omitted_hits) are reported in the
telemetrysession_endevent instead.exec_commandandread_outputtool descriptions now direct clients to
redirect very large output to a file and page it withread_file/
search_text.- A method this server does not implement now returns
-32601before the
handshake as well as after it. Such a call previously returned-32002 Server not initialized, which tells a client to handshake and retry a method that
will never exist. - Breaking: HTTP is now stateless. The server no longer issues an
Mcp-Session-Id, and a request that returns one from an older server is
served normally instead of being refused with-32001 Unknown MCP session.
Every request is answered by the one runtime that owns the workspace, so the
128-session ceiling and its503, the session idle expiry, and the
MCP-Protocol-Version-must-match-the-session check are all gone. - Breaking:
DELETE /mcpnow returns405withAllow: POST; there is no
session to terminate.DELETEis no longer advertised in theAllow,
Access-Control-Allow-Methods, or server cardtransport.methodslists. - Breaking: the handshake is no longer an admission gate.
tools/list,
tools/call, and the other implemented methods are served whether or not
the caller sentinitializefirst, and-32002 Server not initializedis
never returned.initializeis now idempotent: each one negotiates a
version on its own and answers with it, so a repeat that names a different
supported version is answered with that version instead of-32600 Server is already initialized with a different protocol version. - Breaking:
server_inforeportssupported_protocol_versions(every
version this server speaks, newest first) in place ofprotocol_version
(the one version a session had negotiated), and the server card at
/.well-known/mcp.jsonreportssupportedProtocolVersionsin place of
protocolVersion. Neither is a session-scoped value any more. - Runtime state a shared server exposes to concurrent requests is hardened:
the runtime directory (and theHOME,TMPDIR, and cache directories under
it) is resolved to the primary or fallback location exactly once, so a later
failure reportsRUNTIME_DIR_UNWRITABLEinstead of moving a running
command's directories, and the non-git diff fallback snapshots its patch
baselines under the patch lock. - The
devextra now installs the official MCP python SDK (mcp). The
compliance suite drives this server with it over both transports, which is
the only check in the suite that does not use a client we wrote ourselves. - Telemetry now measures the process rather than one client's handshake. A
session is activated by the first request or notification that passes
envelope validation — in either era, and before the method runs, so a first
call that fails still reports it — whilepingnever activates one, leaving
an HTTP health probe against an idle server silent. Client identity moves
with the request that carried it:initializeemits its ownhandshake
event with the negotiated version and theclientInfoit was given, a
2026-07-28tool_errorcarries the sanitizedclientInfoof that
request, and no other event claims to know who is calling.
consecutive_failuresand the 20-error budget are runtime-wide across every
client,session_endadds per-era request counts and aserver/discover
probe count, and the retained-output counters thatserver_infoused to
report travel with it. Each protocol choice a process first serves is also
logged as one line on stderr, telemetry on or off. See
docs/telemetry.md. - Behavior change:
initializeno longer fails with-32602when a client
asks for aprotocolVersionthis server does not speak. As the handshake
spec requires, the server now answers with anInitializeResultnaming the
newest version it does speak (2025-11-25); a client that asks for a
supported version still gets that version back. Asking to handshake with
2026-07-28downgrades the same way, because that protocol states its
version per request instead of negotiating one. - Behavior change: an HTTP request without an
MCP-Protocol-Versionheader
is treated as2025-11-25, the newest handshake version this server speaks.
The older spec suggests assuming2025-03-26, which this server has never
spoken. The header value travels with the request as context and is available
to the runtime; nothing echoes it, records it, or acts on it, and no method
behaves differently for it. - The runtime contract is now
docs/runtime-contract-v0.3.md, and
docs/migration-0.3.md collects every breaking change
above with what to do about it. The v0.2 contract is kept, frozen, as the
0.2.x record.
Added
- Support for MCP
2026-07-28, which serves a request without a handshake.
Such a request states its own protocol version inparams._meta
(io.modelcontextprotocol/protocolVersionand
io.modelcontextprotocol/clientCapabilitiesare required,
io.modelcontextprotocol/clientInfois optional) and may call
server/discover,ping,tools/list, andtools/callimmediately. A
_metaversion this server does not speak is answered with-32022and the
versions it does
(data.supported); a missing or mistyped required_metafield is answered
with-32602. Requests without that_metakey — including legacy requests
that carry_meta.progressToken, and everyinitialize— keep the
handshake behavior they had. server/discoveranswers the probe a2026-07-28client sends instead of a
handshake, so such a client never has to send one: it reports the versions
this server speaks per request (["2026-07-28"]alone, since naming a
handshake-era version here would invite the client to put one in its
_meta, where it is unsupported), thetoolscapability, and the same
workspace instructionsinitialize...
v0.3.0
0.3.0 - 2026-08-13
Changed
- Breaking: command handles are now named
command_id;kill_sessionis
nowkill_command; retained output references use
command:<command_id>:stdout|stderr. The former commandsession_id,
kill_session, andsession:output references are not accepted. - Commands and retained output are now owned by the workspace server rather
than by an individualMcp-Session-Id. Multiple authenticated clients for
the same workspace can continue, read, or terminate a command after
reconnecting or creating a new MCP transport session using thecommand_id
returned byexec_command. - Closing or expiring an HTTP transport session no longer terminates workspace
commands. Commands are still bounded by the existing active-count, retained
output, byte, timeout, and TTL limits and are terminated when the workspace
server shuts down. - Breaking:
notifications/cancelledno longer terminates the command the
cancelled request started. The notification is still accepted and, as
before, answered with no response. A command outlives the request that
started it and is shared by every client of the workspace, and the mapping
was keyed by the client's own JSON-RPC id, so two clients that both used
id: 1could cancel each other's commands. Terminate a command with
kill_command; the reduced cancellation responsiveness is tracked in issue
#48. - Breaking:
get_default_cwdandset_default_cwdare removed and the
default catalog is now 18 tools. A relativepathalways resolves against
the workspace root, so there is no session-scoped working directory to set,
read, or lose on reconnect. Pass a workspace-relativepath, or
exec_command'sworkdir, to target a subdirectory. Theread_file
next_actioncontinuation now repeats the workspace-relative path it was
given rather than one relative to a session cwd, andserver_infono longer
reportsdefault_cwd. - Tool descriptions now direct remote clients to pass explicit
path/workdir
arguments and include concrete examples for patching and command
continuation. - Error text now names the error's category and whether it is retryable, and
tells a model not to repeat a call that cannot succeed.retryableand
categorywere only ever instructuredContent, which most clients do not
forward to the model, so a permanent failure was indistinguishable from a
transient one. COMMAND_NOT_FOUNDfromwrite_stdin,kill_command, andread_outputnow
explains that the handle expired or never existed, states the retention
window a finished command's output has, and namesexec_commandas the way
to recover. Retrying a dead handle is the single largest source of failed
write_stdincalls.kill_commandnow declareskill_wait_ms(hard-kill escalation wait,
default 2000 ms) in its input schema; previously the runtime honored it but
schema validation rejected any call that passed it.read_outputno longer accepts the undocumentedcommand:<id>:full
reference form, which silently read stdout only. Use the per-stream
command:<id>:stdout/command:<id>:stderrreferences.- Retained command output now keeps the earliest bytes per stream (a frozen
head segment, one eighth of the per-stream budget) in addition to the
rolling tail, so the command echo and first errors survive large outputs.
read_outputreportshead_retained_bytesandevicted_gap_bytes. server_infoexposes anoutput_retentionblock naming the per-stream
retention budget (buffer_bytes_per_stream,head_bytes_per_stream). How
often that budget was actually hit is a runtime-wide measurement rather than
an answer to one client, so the eviction counters (evict_events,
evicted_bytes_total) and omitted-read counters
(read_output_omitted_hits,poll_omitted_hits) are reported in the
telemetrysession_endevent instead.exec_commandandread_outputtool descriptions now direct clients to
redirect very large output to a file and page it withread_file/
search_text.- A method this server does not implement now returns
-32601before the
handshake as well as after it. Such a call previously returned-32002 Server not initialized, which tells a client to handshake and retry a method that
will never exist. - Breaking: HTTP is now stateless. The server no longer issues an
Mcp-Session-Id, and a request that returns one from an older server is
served normally instead of being refused with-32001 Unknown MCP session.
Every request is answered by the one runtime that owns the workspace, so the
128-session ceiling and its503, the session idle expiry, and the
MCP-Protocol-Version-must-match-the-session check are all gone. - Breaking:
DELETE /mcpnow returns405withAllow: POST; there is no
session to terminate.DELETEis no longer advertised in theAllow,
Access-Control-Allow-Methods, or server cardtransport.methodslists. - Breaking: the handshake is no longer an admission gate.
tools/list,
tools/call, and the other implemented methods are served whether or not
the caller sentinitializefirst, and-32002 Server not initializedis
never returned.initializeis now idempotent: each one negotiates a
version on its own and answers with it, so a repeat that names a different
supported version is answered with that version instead of-32600 Server is already initialized with a different protocol version. - Breaking:
server_inforeportssupported_protocol_versions(every
version this server speaks, newest first) in place ofprotocol_version
(the one version a session had negotiated), and the server card at
/.well-known/mcp.jsonreportssupportedProtocolVersionsin place of
protocolVersion. Neither is a session-scoped value any more. - Runtime state a shared server exposes to concurrent requests is hardened:
the runtime directory (and theHOME,TMPDIR, and cache directories under
it) is resolved to the primary or fallback location exactly once, so a later
failure reportsRUNTIME_DIR_UNWRITABLEinstead of moving a running
command's directories, and the non-git diff fallback snapshots its patch
baselines under the patch lock. - The
devextra now installs the official MCP python SDK (mcp). The
compliance suite drives this server with it over both transports, which is
the only check in the suite that does not use a client we wrote ourselves. - Telemetry now measures the process rather than one client's handshake. A
session is activated by the first request or notification that passes
envelope validation — in either era, and before the method runs, so a first
call that fails still reports it — whilepingnever activates one, leaving
an HTTP health probe against an idle server silent. Client identity moves
with the request that carried it:initializeemits its ownhandshake
event with the negotiated version and theclientInfoit was given, a
2026-07-28tool_errorcarries the sanitizedclientInfoof that
request, and no other event claims to know who is calling.
consecutive_failuresand the 20-error budget are runtime-wide across every
client,session_endadds per-era request counts and aserver/discover
probe count, and the retained-output counters thatserver_infoused to
report travel with it. Each protocol choice a process first serves is also
logged as one line on stderr, telemetry on or off. See
docs/telemetry.md. - Behavior change:
initializeno longer fails with-32602when a client
asks for aprotocolVersionthis server does not speak. As the handshake
spec requires, the server now answers with anInitializeResultnaming the
newest version it does speak (2025-11-25); a client that asks for a
supported version still gets that version back. Asking to handshake with
2026-07-28downgrades the same way, because that protocol states its
version per request instead of negotiating one. - Behavior change: an HTTP request without an
MCP-Protocol-Versionheader
is treated as2025-11-25, the newest handshake version this server speaks.
The older spec suggests assuming2025-03-26, which this server has never
spoken. The header value travels with the request as context and is available
to the runtime; nothing echoes it, records it, or acts on it, and no method
behaves differently for it. - The runtime contract is now
docs/runtime-contract-v0.3.md, and
docs/migration-0.3.md collects every breaking change
above with what to do about it. The v0.2 contract is kept, frozen, as the
0.2.x record.
Added
- Support for MCP
2026-07-28, which serves a request without a handshake.
Such a request states its own protocol version inparams._meta
(io.modelcontextprotocol/protocolVersionand
io.modelcontextprotocol/clientCapabilitiesare required,
io.modelcontextprotocol/clientInfois optional) and may call
server/discover,ping,tools/list, andtools/callimmediately. A
_metaversion this server does not speak is answered with-32022and the
versions it does
(data.supported); a missing or mistyped required_metafield is answered
with-32602. Requests without that_metakey — including legacy requests
that carry_meta.progressToken, and everyinitialize— keep the
handshake behavior they had. server/discoveranswers the probe a2026-07-28client sends instead of a
handshake, so such a client never has to send one: it reports the versions
this server speaks per request (["2026-07-28"]alone, since naming a
handshake-era version here would invite the client to put one in its
_meta, where it is unsupported), thetoolscapability, and the same
workspace instructionsinitializeret...
v0.2.3
0.2.3 - 2026-08-13
Fixed
- A repeated
initializeon one persistent STDIO session now replays the
negotiated handshake result instead of failing with-32600 Server is already initialized. The initializer does not run again, so no session
state is reset and no second telemetry session is recorded. Connectors that
sendinitializetwice on the same process — the OpenAI Secure MCP Tunnel
probes withserver/discoverand then initializes twice — had their tool
scan aborted, surfacing as HTTP 424, even though the session was healthy
(issue #39).
v0.2.2
0.2.2 - 2026-07-28
Fixed
- Dynamic client registration no longer rejects clients that request grant or
response types the server does not implement. Requested values are now
narrowed to the supported set and reported back in the registration
response, per RFC 7591, instead of failing the registration with
invalid_client_metadata. Clients that register with
grant_types: ["authorization_code", "refresh_token"]— the common case —
could not connect at all.
Changed
- Raised the default OAuth access token lifetime from one hour to 24 hours
(CODING_TOOLS_MCP_OAUTH_TOKEN_TTL, still capped at 604800). The server
does not issue refresh tokens, so a one-hour default forced re-authorization
every hour. - Advertised grant and response types now derive from single constants shared
by authorization-server metadata and the registration endpoint.
v0.2.1
0.2.1 - 2026-07-26
Changed
- Replaced the manual multi-dispatch release choreography with a single
release.ymlpipeline triggered by pushing av<version>tag. The evidence
workflows (compliance,real-workloads,swebench-lite) run as called
jobs of the same workflow run as the registry publishes, so the
same-release-commit audit property holds by construction with no inputs, run
ids, or ref choices; the npm launcher publishes only when its version is not
already on the registry; the GitHub Release is created automatically with
notes from the CHANGELOG section. The dispatch-onlypublish-pypiand
publish-npmworkflows were removed, the npm tarball is published by local
path (the previous form was parsed as a GitHub shorthand and failed), and
final-auditremains as a manual audit tool outside the release path. PyPI
and npm trusted publishing must point atrelease.yml.
Added
- Anonymous usage telemetry, on by default, sent to PostHog over HTTPS with
the standard library only. Events are a closed schema of counters, enums,
durations, and version/platform strings — session starts, per-tool
success/error/latency summaries, and individual tool failures capped at 20
per session — and never contain paths, arguments, command lines, or file
contents.CODING_TOOLS_MCP_TELEMETRY=offorDO_NOT_TRACK=1disables it,
CODING_TOOLS_MCP_TELEMETRY=debugprints events to stderr instead of
sending, CI environments are excluded automatically, and the anonymous
install id in~/.coding-tools-mcp/idcan be deleted to reset identity.
Tool calls never block on delivery: events queue in memory to a daemon
thread and failures are dropped silently. Seedocs/telemetry.md.
v0.2.0
0.2.0 - 2026-07-24
Changed
- Replaced selectable tool profiles with one stable, truthfully annotated
coding catalog. Permission modes still control command policy but never alter
tools/list. - Made
apply_patchthe sole direct file-mutation tool and added staged,
baseline-checked, same-directory atomic replacement with multi-file rollback,
mode/BOM/newline preservation, and structured retry errors. - Changed model-facing
contentfrom a JSON mirror to per-tool summaries and
previews sized by each tool's own per-call limits, without the former 16 KiB
renderer preview cap. A generous emergency ceiling still protects clients
from unbounded individual entries. Command results always lead with
status/exit code; pageable truncation names executable continuation calls,
while non-pageable results state how to narrow or raise their limits. Clients
that parsed text as JSON must readstructuredContent. - Changed
exec_commandandwrite_stdindefault yield to 10 seconds. Running
or truncated results now provide explicit machine-readablenext_action. - Split active and retained process sessions and added concurrency, count, byte,
and TTL limits. POSIX TTY requests now use a real PTY; Windows reports an
explicit unsupported error in this build and uses portable graceful/forced
process termination without assumingSIGKILLexists. - Upgraded the primary protocol to MCP
2025-11-25while retaining explicit
2025-06-18compatibility.
Added
- Independent per-
Mcp-Session-IdHTTP runtimes, session termination, standard
cancellation mapping, batch rejection, and strict protocol-header checks. - OAuth protected-resource metadata, Authorization Code + PKCE S256, exact
redirect binding, one-hour client-bound tokens, and RFC 7591 dynamic client
registration. - Automatic bounded root project-instruction loading during initialization.
- Streaming/bounded file reads, early-stopping ripgrep, batched Git ignore
checks, and iterator-based traversal. - Dedicated patch, process, result, project-context, OAuth, error, and HTTP
session modules plus regression coverage for their boundary conditions. - Reproducible dogfood efficiency metrics and a five-run 0.1.7/0.2.0 comparison;
serialized tool-result bytes fell 37.279% with unchanged completion and call
counts on the deterministic workload. - Desktop client MVP, shipped in the same distribution as the server and
exposed as thecoding-tools-mcp-desktopentry point behind the optional
[desktop]extra: per-workspace profiles, local server start/stop, FRP and
Cloudflare tunnel modes (quick URL or named fixed domain), OAuth and bearer
credential setup with clipboard helpers, and concurrent health checks across
local and public.well-known/mcp.jsonplus OAuth authorization-server and
protected-resource metadata. - Desktop profile storage under
~/.coding-tools-mcp-desktopthat keeps
secrets in a separate file from profiles, writes both throughfsyncand
atomic replacement with0600files and0700directories, validates
profile IDs before deriving state paths, and drops unknown keys so records
written by other releases keep loading. - Desktop English and Simplified Chinese catalogs that follow the system
language on first launch and switch at runtime, a
scripts/check_desktop_i18n.pycoverage and placeholder gate wired into
make lint, andmake desktop-i18n-update,-release, and-check. - npm launcher package for
npx coding-tools-mcp. It starts the PyPI server
throughuvxorpipx run, forwards arguments and stdio, mirrors fatal
signals instead of remapping them to exit codes, and prints actionable
install steps when neither runner is onPATH. Its own version is
independent of the server version, whichCODING_TOOLS_MCP_VERSIONpins. - Cloudflare sandbox control Worker in
cloudflare/sandbox-control: an
authenticated control plane that dispatches
.github/workflows/start-sandbox.ymlthroughPOST /startand through an
MCP JSON-RPC endpoint at/mcpexposingstart_coding_tools_sandboxand
get_coding_tools_sandbox_status. It answersinitialize,ping,
resources/list, andprompts/list, acknowledges notifications with202,
and reports GitHub dispatch failures with the ref and workflow that failed.
The Worker never runs code and never proxies MCP traffic. start-sandboxworkflow inputs fortunnel_type,tunnel_hostname,
auth_token, andhide_auth_token, so a sandbox can publish one reusable
Cloudflare named hostname with a secret-managed bearer token kept out of
workflow logs and run summaries.--dangerously-fake-readonly-annotationsand
CODING_TOOLS_MCP_DANGEROUSLY_FAKE_READONLY_ANNOTATIONS, which report every tool
intools/listas read-only and non-destructive. This restores the one capability
lost when thecompat-readonly-allprofile was removed: a client that gates on
annotations is unreachable from server-side permission modes, sodangerousmode
cannot quiet it. Unlike the old profile the catalog is untouched and the claim is
fenced in — it requiresdangerouspermission mode, requires authentication over
HTTP because a tunnel forwards to a loopback bind, and is confined totools/list.
server_info.annotation_override, the server card'stools.annotationOverride,
andcheck_exec_environmentall report it, and bothserver_infoand the server
card keep publishing the real per-tool annotations.- A
deploy-sandbox-controlworkflow that deploys the Cloudflare control-plane
Worker on every push touchingcloudflare/sandbox-control/**or
start-sandbox.yml. Nothing deployed the Worker before, so its half of the
workflow_dispatchcontract could sit unshipped indefinitely while the workflow
half moved on, which GitHub rejects with422 Unexpected inputs providedbefore
creating a run. make check-dispatch-inputs, which compares the Worker's dispatch body against
start-sandbox.yml's declaredworkflow_dispatchinputs and cross-checks
workflow_call. It gates the deploy and runs inmake ci.
Removed
--tool-profile,CODING_TOOLS_MCP_TOOL_PROFILE, and all launcher/UI/control
plane profile selectors.- Duplicate image base64/data URLs and the
view_image.outputselector. Image
bytes now appear once in one MCP image block. - JSON-RPC batch handling and the unimplemented logging capability declaration.
Fixed
- Lowercase the owner segment when composing the GHCR sandbox image tag, so
builds from mixed-case repository owners resolve to a valid image reference.
Security
- Public tunnel documentation no longer recommends anonymous read-only mode:
the fixed catalog includes mutation and execution, so remote access must be
authenticated. - Forwarded headers are trusted only when explicitly enabled; browser origins,
OAuth resources, clients, redirect URIs, and token auth methods are bound
exactly.