Skip to content

feat(tools)!: report facts, not advice, from execute_anonymous - #115

Merged
lcottercertinia merged 11 commits into
mainfrom
feat/109-execute-anonymous-shape
Aug 12, 2026
Merged

feat(tools)!: report facts, not advice, from execute_anonymous#115
lcottercertinia merged 11 commits into
mainfrom
feat/109-execute-anonymous-shape

Conversation

@lukecotter

Copy link
Copy Markdown
Collaborator

Stacked on #114.

execute_anonymous carried a sentence telling the caller to write a .gitignore entry, on the runs where the output directory was new. That is a rule applied to a fact the caller could not see.

It now states the fact — outputDirCreated — and lets the agent apply its own rule. Four tokens where the sentence was fifteen. It is reported either way, not only when true: an absent field cannot be told apart from one the server never worked out.

success is now succeeded, the bare adjective every other boolean in the server uses.

Closes #109

Record the rules the v2 rename follows: the apexlog_ prefix, a verb that
states the shape of the result, banned verbs, and eight field rules.
Add "don't state what the caller can derive" to the shaping conventions.

Refs #107
Every failure to open the log reported "Log file not found", so a
permission error, a directory in place of a file, or an exhausted
descriptor table all sent the caller to look for a file that was there.
Report ENOENT as missing, name the code otherwise, and keep the original
error as `cause`.

Refs #109
All three analysis tools documented an absolute path and accepted any
string. A relative one resolved against the server's working directory,
which is where the client spawned the server and not where the caller is,
so it read a different file or none and reported neither.

Declare the path once in apexLogSource, the module the three tools already
share for loading a log, and refine it to absolute. Refinements do not
reach the JSON schema, so the tool definitions are unchanged at ~1,053
tokens.

Resolve execute_anonymous's outputDir to absolute too, so the filePath it
returns stays one the analysis tools accept. A relative outputDir now
anchors to the project root, the base the default already used.

Refs #109
Only SIGINT was trapped, so a supervised restart, a container stop, or a
client that ends a stdio server hit Node's default and exited without the
clean close.

Refs #109
execute_anonymous created directories anywhere the process could write,
from an agent-supplied path. The MCP spec expects a server to work inside
the roots the client declares, so report rather than refuse: the log is
still written, and both the response and stderr name where it went.

Silent for the default outputDir, for one inside a root, and for a client
that declares no roots. Symlinks are followed on both sides.

Refs #109
isMethodNode kept EXECUTION_STARTED and ENTERING_MANAGED_PKG, which own no
time, and dropped every query, DML, flow and workflow node, which do. This is
the one classification the tools will share, with grouping so a query repeated
in a loop is one row carrying its call count.

Nothing calls it yet.

Refs #108
find_performance_bottlenecks returned four hand-built sections that
covered six of the thirteen limits, repeated the CPU figure as prose,
and ranked methods by namespace — a second, disagreeing answer to the
question analyze_apex_log_performance already answers.

It now returns one table: the limits at or above a threshold, worst
first, as {limit, used, max, usedPercentage} rows. All thirteen are
covered. The table is reported even when empty, beside the threshold
that selected it, so "nothing is at risk" is an answer rather than a
gap.

BREAKING CHANGE: the analysisType parameter is removed and replaced by
threshold. The cpuBottlenecks, databaseBottlenecks, methodBottlenecks
and governorLimitWarnings sections and the note field are gone.

Refs #108
analyze_apex_log_performance ranked methods, so the time a
transaction spent in queries, DML, flows and managed packages
was invisible. It now ranks every timed operation by self time
in one table, with kind, namespace, limit, minSelfMs and
groupBy to select and fold the rows.

The prose summary and the recommendations are gone: the table
says what they said. returnedSelfPercentage carries the one
fact the rows cannot — what share of the transaction they
account for between them.

The definition costs 32% more, for the five selection
parameters. The response costs 33% less on the governor-heavy
log and 54% less on the minimal one.

Closes #108
get_apex_log_summary reported five hand-picked totals and only the limits
something was spent against. It now reports where the time went, one row
per kind of operation, and what each namespace consumed.

timeByKind names the trace category that decides whether a kind reaches
the log, so a zero can be read: soql 0 beside DB NONE means the queries
were not logged, and beside DB FINEST means none ran.

limitsByNamespace shows a managed package spending your CPU time, which
no tool could show before.

truncated reads the top-level lines. The parser marks the line that lost
its exit event, not the log, so the root flag is always false.

The response costs 16% more on a log that uses its limits, for the two
tables. No fact was dropped to pay for them.

Closes #62
The response carried a sentence telling the caller to write a .gitignore
entry, on the runs where the output directory was new. That is a rule
applied to a fact the caller could not see. It now states the fact,
outputDirCreated, and lets the agent apply its own rule. Four tokens
where the sentence was fifteen.

It is reported either way, not only when true. An absent field cannot be
told apart from one the server never worked out.

success is now succeeded, which is how every other boolean in the server
reads.

Closes #109
Base automatically changed from feat/108-rank-operations to main August 12, 2026 13:53
Conflicts from the parse-once work landing on main:

- src/tools/executeAnonymous.ts — keep the shorter mkdir comment. Main's
  version still points at the .gitignore tip this branch removed.
- CLAUDE.md — keep the outputDirCreated sentence.
- CHANGELOG.md — keep the execute_anonymous succeeded/outputDirCreated
  entry, and the unconditional 30% saving. Dropping the tip made it no
  longer "after the first run".
@lcottercertinia
lcottercertinia added this pull request to the merge queue Aug 12, 2026
@lcottercertinia
lcottercertinia removed this pull request from the merge queue due to a manual request Aug 12, 2026
@lcottercertinia
lcottercertinia added this pull request to the merge queue Aug 12, 2026
Merged via the queue into main with commit e1203be Aug 12, 2026
15 checks passed
@lcottercertinia
lcottercertinia deleted the feat/109-execute-anonymous-shape branch August 12, 2026 14:55
lukecotter added a commit that referenced this pull request Aug 12, 2026
Main squashed #114 and #115, which this branch already carries under the
new tool names, so every conflict keeps this side. The old tool files are
byte-identical to the ones renamed here, so the deletions stand.

The one real change from main is #118: merge_group on the required
workflows.
lukecotter added a commit to lukecotter/debug-log-analyzer-mcp that referenced this pull request Aug 12, 2026
…inia#116)

Stacked on certinia#115.

An unprefixed name collides between servers: `get_issue` and
`list_issues` ship in both the GitHub and the Sentry server. A verb that
names the work rather than the result cannot be told apart from another
tool's — and `analyze`, `find` and `detect` all name work.

| 1.x | 2.0 |
| --- | --- |
| `get_apex_log_summary` | `apexlog_get_summary` |
| `analyze_apex_log_performance` | `apexlog_list_slow_operations` |
| `find_performance_bottlenecks` | `apexlog_list_limit_risks` |
| `execute_anonymous` | `apexlog_execute_anonymous` |

The modules and their exported symbols follow the tool they serve:
`analyzeLogPerformance.ts` is `listSlowOperations.ts`,
`findPerformanceBottlenecks.ts` is `listLimitRisks.ts`, and the golden
files are named for their tool.

The 1.0.0 changelog section keeps the names that shipped.

The definitions now cost ~1,128 tokens, one more than before — the
longer names.

Closes certinia#107
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: read-error cause, relative paths, outputDir outside the client roots, SIGTERM

2 participants