@evref-bl/pharo-mcp 0.0.0-stage → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -2
- package/bin/pharo-mcp.js +22 -0
- package/node/index.js +1 -0
- package/node/installer.js +151 -0
- package/package.json +33 -4
- package/templates/.claude/mcp.json +8 -0
- package/templates/.codex/config.toml +4 -0
- package/templates/.opencode/opencode.json +10 -0
- package/templates/pharo-references/debugger-driven-development.md +101 -0
- package/templates/pharo-references/debugging.md +147 -0
- package/templates/pharo-references/getting-started.md +143 -0
- package/templates/pharo-references/pharo-coding-rules.md +147 -0
- package/templates/pharo-references/pharo-coding-style.md +139 -0
- package/templates/pharo-references/safety-and-ecosystem.md +256 -0
- package/templates/pharo-references/source-vs-live-image.md +75 -0
- package/templates/pharo-references/tool-reference.md +273 -0
- package/templates/pharo-references/troubleshooting.md +137 -0
- package/templates/pharo-references/using-pharo-mcp.md +156 -0
- package/templates/pharo-skills/pharo-ci-repro/SKILL.md +46 -0
- package/templates/pharo-skills/pharo-ci-repro/agents/openai.yaml +4 -0
- package/templates/pharo-skills/pharo-ci-repro/references/pharo-smalltalkci.md +102 -0
- package/templates/pharo-skills/pharo-ci-repro/scripts/run-smalltalkci-docker.sh +172 -0
- package/templates/pharo-skills/pharo-image-git-handoff/SKILL.md +37 -0
- package/templates/pharo-skills/pharo-image-git-handoff/agents/openai.yaml +4 -0
- package/templates/pharo-skills/pharo-mcp-recovery/SKILL.md +34 -0
- package/templates/pharo-skills/pharo-mcp-recovery/agents/openai.yaml +4 -0
- package/templates/pharo-skills/pharo-project-load/SKILL.md +47 -0
- package/templates/pharo-skills/pharo-project-load/agents/openai.yaml +4 -0
- package/templates/pharo-skills/pharo-version-compat/SKILL.md +57 -0
- package/templates/pharo-skills/pharo-version-compat/agents/openai.yaml +4 -0
- package/templates/pharo.AGENTS.md +42 -0
package/README.md
CHANGED
|
@@ -1,3 +1,80 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://github.com/pharo-project/Pharo)
|
|
2
|
+
[](https://github.com/Evref-BL/MCP/actions/workflows/ci.yml?query=branch%3Amain+event%3Apush)
|
|
3
|
+
[](https://coveralls.io/github/Evref-BL/MCP?branch=main)
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
# MCP for Pharo
|
|
6
|
+
|
|
7
|
+
MCP is an in-image [Model Context Protocol](https://modelcontextprotocol.io/)
|
|
8
|
+
server for Pharo. It lets MCP clients inspect and change the running image
|
|
9
|
+
through Pharo-aware tools for packages, classes, methods, tests, repositories,
|
|
10
|
+
change history, debugging, and UI inspection.
|
|
11
|
+
|
|
12
|
+
MCP works through the Pharo ecosystem—compilation, refactorings, Renraku,
|
|
13
|
+
SUnit, Iceberg, Metacello, Epicea, and Spec—rather than patching exported Tonel
|
|
14
|
+
files as plain text.
|
|
15
|
+
|
|
16
|
+
## Supported Versions
|
|
17
|
+
|
|
18
|
+
Pharo 12, 13, and 14 are supported. Development targets Pharo 13 while CI
|
|
19
|
+
checks the full matrix.
|
|
20
|
+
|
|
21
|
+
## Install And Start
|
|
22
|
+
|
|
23
|
+
Load MCP in a supported image:
|
|
24
|
+
|
|
25
|
+
```smalltalk
|
|
26
|
+
Metacello new
|
|
27
|
+
baseline: 'MCP';
|
|
28
|
+
repository: 'github://Evref-BL/MCP:main/src';
|
|
29
|
+
load.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Start the Streamable HTTP server:
|
|
33
|
+
|
|
34
|
+
```smalltalk
|
|
35
|
+
mcp := MCP new.
|
|
36
|
+
mcp port: 4000.
|
|
37
|
+
mcp start.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Connect a local MCP client to `http://127.0.0.1:4000`. Client configuration
|
|
41
|
+
formats differ; see [Getting started](templates/pharo-references/getting-started.md) for setup
|
|
42
|
+
and first calls.
|
|
43
|
+
|
|
44
|
+
## Documentation
|
|
45
|
+
|
|
46
|
+
- [Documentation index](docs/README.md)
|
|
47
|
+
- [Getting started](templates/pharo-references/getting-started.md)
|
|
48
|
+
- [Using MCP from an agent](templates/pharo-references/using-pharo-mcp.md)
|
|
49
|
+
- [Tool reference](templates/pharo-references/tool-reference.md)
|
|
50
|
+
- [Safety and ecosystem integration](templates/pharo-references/safety-and-ecosystem.md)
|
|
51
|
+
- [Troubleshooting](templates/pharo-references/troubleshooting.md)
|
|
52
|
+
- [Developing MCP](docs/developing-mcp.md)
|
|
53
|
+
|
|
54
|
+
## Agent Templates
|
|
55
|
+
|
|
56
|
+
Install the guide, local references, workflow skills, and direct MCP
|
|
57
|
+
configuration for an agent host into the current project:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
npx @evref-bl/pharo-mcp install --host codex
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Use `--host claude` or `--host opencode` for those hosts. The direct MCP
|
|
64
|
+
configuration expects port `4000`; update it when the image uses another port.
|
|
65
|
+
|
|
66
|
+
Node integrations that provide their own MCP transport can install only the
|
|
67
|
+
guide, skills, references, and soft-loading instruction block:
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
import { installGuidance } from "@evref-bl/pharo-mcp";
|
|
71
|
+
|
|
72
|
+
installGuidance({ host: "codex", cwd: process.cwd() });
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Contributing
|
|
76
|
+
|
|
77
|
+
Use Pharo 13 for normal development and preserve Pharo 12/14 compatibility.
|
|
78
|
+
Live behavior must be verified in an image; source inspection alone is not
|
|
79
|
+
enough. Start with [AGENTS.md](AGENTS.md) and
|
|
80
|
+
[MCP verification](docs/mcp-verification.md).
|
package/bin/pharo-mcp.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { install } from "../node/index.js";
|
|
4
|
+
|
|
5
|
+
function usage() {
|
|
6
|
+
return "Usage: pharo-mcp install --host <claude|codex|opencode>";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const [command, option, host, ...extra] = process.argv.slice(2);
|
|
10
|
+
|
|
11
|
+
if (command !== "install" || option !== "--host" || !host || extra.length > 0) {
|
|
12
|
+
console.error(usage());
|
|
13
|
+
process.exitCode = 1;
|
|
14
|
+
} else {
|
|
15
|
+
try {
|
|
16
|
+
const result = install({ host });
|
|
17
|
+
console.log(`Installed Pharo MCP templates for ${result.host} in ${result.cwd}.`);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error(error.message);
|
|
20
|
+
process.exitCode = 1;
|
|
21
|
+
}
|
|
22
|
+
}
|
package/node/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { install, installGuidance, templateDirectory } from "./installer.js";
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const hosts = {
|
|
6
|
+
claude: {
|
|
7
|
+
directory: ".claude",
|
|
8
|
+
guide: "CLAUDE.md",
|
|
9
|
+
configuration: [".claude/mcp.json", ".mcp.json"],
|
|
10
|
+
},
|
|
11
|
+
codex: {
|
|
12
|
+
directory: ".codex",
|
|
13
|
+
guide: "AGENTS.md",
|
|
14
|
+
configuration: [".codex/config.toml", ".codex/config.toml"],
|
|
15
|
+
},
|
|
16
|
+
opencode: {
|
|
17
|
+
directory: ".opencode",
|
|
18
|
+
guide: "AGENTS.md",
|
|
19
|
+
configuration: [".opencode/opencode.json", "opencode.json"],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const managedBlock = {
|
|
24
|
+
begin: "<!-- pharo-mcp:begin -->",
|
|
25
|
+
end: "<!-- pharo-mcp:end -->",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function templateDirectory() {
|
|
29
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../templates");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function hostConfiguration(host) {
|
|
33
|
+
const configuration = hosts[host];
|
|
34
|
+
if (!configuration) {
|
|
35
|
+
throw new Error(`Unknown agent host: ${host}. Use claude, codex, or opencode.`);
|
|
36
|
+
}
|
|
37
|
+
return configuration;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function filesIn(directory) {
|
|
41
|
+
return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
42
|
+
const source = path.join(directory, entry.name);
|
|
43
|
+
return entry.isDirectory() ? filesIn(source) : [source];
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function directoryPlan(sourceRelativePath, targetRelativePath) {
|
|
48
|
+
const sourceDirectory = path.join(templateDirectory(), sourceRelativePath);
|
|
49
|
+
return filesIn(sourceDirectory).map((source) => ({
|
|
50
|
+
source,
|
|
51
|
+
target: path.join(targetRelativePath, path.relative(sourceDirectory, source)),
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function filePlan(sourceRelativePath, targetRelativePath) {
|
|
56
|
+
return [{
|
|
57
|
+
source: path.join(templateDirectory(), sourceRelativePath),
|
|
58
|
+
target: targetRelativePath,
|
|
59
|
+
}];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function guidancePlan(configuration) {
|
|
63
|
+
return [
|
|
64
|
+
...filePlan("pharo.AGENTS.md", path.join(configuration.directory, "pharo.AGENTS.md")),
|
|
65
|
+
...directoryPlan("pharo-skills", path.join(configuration.directory, "skills")),
|
|
66
|
+
...directoryPlan("pharo-references", path.join(configuration.directory, "pharo-references")),
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function fullPlan(configuration) {
|
|
71
|
+
const [source, target] = configuration.configuration;
|
|
72
|
+
return [...guidancePlan(configuration), ...filePlan(source, target)];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function absolutePlan(cwd, plan) {
|
|
76
|
+
return plan.map(({ source, target }) => ({ source, target: path.resolve(cwd, target) }));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function assertCompatible(plan) {
|
|
80
|
+
for (const { source, target } of plan) {
|
|
81
|
+
if (!fs.existsSync(target)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!fs.statSync(target).isFile() || !fs.readFileSync(source).equals(fs.readFileSync(target))) {
|
|
85
|
+
throw new Error(`Refusing to replace existing file: ${target}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function copyMissing(plan) {
|
|
91
|
+
for (const { source, target } of plan) {
|
|
92
|
+
if (fs.existsSync(target)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
96
|
+
fs.copyFileSync(source, target);
|
|
97
|
+
fs.chmodSync(target, fs.statSync(source).mode & 0o777);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function bridgeContent(directory) {
|
|
102
|
+
return `${managedBlock.begin}\nWhen the task uses Pharo, read \`${directory}/pharo.AGENTS.md\` before acting.\n${managedBlock.end}\n`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function updatedGuide(current, block) {
|
|
106
|
+
const starts = current.includes(managedBlock.begin);
|
|
107
|
+
const ends = current.includes(managedBlock.end);
|
|
108
|
+
if (starts !== ends) {
|
|
109
|
+
throw new Error("The existing Pharo MCP instruction block is incomplete.");
|
|
110
|
+
}
|
|
111
|
+
if (starts) {
|
|
112
|
+
const expression = new RegExp(`${managedBlock.begin}[\\s\\S]*?${managedBlock.end}\\n?`);
|
|
113
|
+
return current.replace(expression, block);
|
|
114
|
+
}
|
|
115
|
+
return current ? `${current.trimEnd()}\n\n${block}` : block;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function guidePlan(cwd, configuration) {
|
|
119
|
+
const target = path.join(cwd, configuration.guide);
|
|
120
|
+
const current = fs.existsSync(target) ? fs.readFileSync(target, "utf8") : "";
|
|
121
|
+
return { target, content: updatedGuide(current, bridgeContent(configuration.directory)) };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function writeGuide({ target, content }) {
|
|
125
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
126
|
+
fs.writeFileSync(target, content);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function prepare(options, direct) {
|
|
130
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
131
|
+
const configuration = hostConfiguration(options.host);
|
|
132
|
+
const plan = absolutePlan(cwd, direct ? fullPlan(configuration) : guidancePlan(configuration));
|
|
133
|
+
const guide = guidePlan(cwd, configuration);
|
|
134
|
+
assertCompatible(plan);
|
|
135
|
+
return { configuration, cwd, guide, plan };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function apply(options, direct) {
|
|
139
|
+
const { configuration, cwd, guide, plan } = prepare(options, direct);
|
|
140
|
+
copyMissing(plan);
|
|
141
|
+
writeGuide(guide);
|
|
142
|
+
return { cwd, host: options.host, directory: configuration.directory };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function installGuidance(options) {
|
|
146
|
+
return apply(options, false);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function install(options) {
|
|
150
|
+
return apply(options, true);
|
|
151
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evref-bl/pharo-mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "Pharo MCP agent-host templates",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": "./node/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"pharo-mcp": "./bin/pharo-mcp.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin",
|
|
13
|
+
"node",
|
|
14
|
+
"templates",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "node --test"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/Evref-BL/MCP.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/Evref-BL/MCP/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/Evref-BL/MCP#readme",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=20"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Debugger-Driven Development
|
|
2
|
+
|
|
3
|
+
Applies when using MCP-Pharo to repair code from a live debugger state.
|
|
4
|
+
|
|
5
|
+
Debugger-driven development keeps execution paused at the failure, edits the
|
|
6
|
+
relevant method, and continues from the debugger instead of restarting the
|
|
7
|
+
whole scenario.
|
|
8
|
+
|
|
9
|
+
## Goal
|
|
10
|
+
|
|
11
|
+
Repair code from the paused execution context while preserving the debugger as
|
|
12
|
+
the source of truth for receiver, selector, class side, selected frame, and
|
|
13
|
+
repair action.
|
|
14
|
+
|
|
15
|
+
## Main Flow
|
|
16
|
+
|
|
17
|
+
1. Get a state with `debug_state_get`, `debug_capture`, or
|
|
18
|
+
`debug_candidate_attach`.
|
|
19
|
+
2. Check `repairActions` before inventing an edit target.
|
|
20
|
+
3. Call `debug_method_update` with the action's `sessionId`, `stateId`, and `frameRef`.
|
|
21
|
+
4. Review returned `critiques`.
|
|
22
|
+
5. If the edit is blocked by critiques and the critiques are acceptable, retry
|
|
23
|
+
with `ignoreCritiques=true`.
|
|
24
|
+
6. Continue with the returned state when execution pauses again.
|
|
25
|
+
|
|
26
|
+
## Must Do
|
|
27
|
+
|
|
28
|
+
- Use the repair action's frame reference instead of selecting a target class or
|
|
29
|
+
method by hand.
|
|
30
|
+
- Review critiques before allowing the repaired computation to proceed.
|
|
31
|
+
- Treat returned states and references as fresh; discard older state-scoped
|
|
32
|
+
references after each edit or control action.
|
|
33
|
+
- Stop after the bounded action the user requested and report the resulting
|
|
34
|
+
state.
|
|
35
|
+
|
|
36
|
+
## Do Not
|
|
37
|
+
|
|
38
|
+
- Do not guess the receiver class, target behavior, selector, or class side for
|
|
39
|
+
missing-method repair.
|
|
40
|
+
- Do not use general `image_evaluate` code to mutate contexts or drive
|
|
41
|
+
debugger UI.
|
|
42
|
+
- Do not continue issuing frame commands when the repair outcome is running,
|
|
43
|
+
timed out, completed, or blocked by critiques.
|
|
44
|
+
|
|
45
|
+
## Missing Methods
|
|
46
|
+
|
|
47
|
+
For `MessageNotUnderstood`, `debug_method_update` creates the method implied by the
|
|
48
|
+
paused message send. The target behavior, selector, class side, and frame are
|
|
49
|
+
derived from the debug state; do not guess them in the request.
|
|
50
|
+
|
|
51
|
+
You may omit `methodSource` when the default generated source is acceptable.
|
|
52
|
+
Provide `methodSource` when the method should do real work. The compiled method
|
|
53
|
+
is formatted before it is returned or used to continue execution.
|
|
54
|
+
|
|
55
|
+
## Stub Methods
|
|
56
|
+
|
|
57
|
+
For `subclassResponsibility`, `shouldBeImplemented`, and `notYetImplemented`,
|
|
58
|
+
`debug_method_update` recompiles the method associated with the selected repair frame.
|
|
59
|
+
Use the repair action's frame reference instead of selecting a target class by
|
|
60
|
+
hand.
|
|
61
|
+
|
|
62
|
+
## Critiques
|
|
63
|
+
|
|
64
|
+
`debug_method_update` uses the same critique gate expected from method-editing tools. It
|
|
65
|
+
stops before proceeding when critiques are reported unless
|
|
66
|
+
`ignoreCritiques=true`.
|
|
67
|
+
|
|
68
|
+
Use `ignoreCritiques=true` only after reading the critiques and deciding that
|
|
69
|
+
continuing the suspended computation is still the right next step.
|
|
70
|
+
|
|
71
|
+
## Proceeding
|
|
72
|
+
|
|
73
|
+
After a successful accepted edit, `debug_method_update` attempts to proceed the repaired
|
|
74
|
+
computation. Outcomes include completed, paused, running, timed out, or blocked
|
|
75
|
+
by critiques.
|
|
76
|
+
|
|
77
|
+
When the outcome is paused, use the returned state and repair actions. When it
|
|
78
|
+
is completed, inspect the result class and print string. When it is running or
|
|
79
|
+
timed out, avoid issuing stale frame commands.
|
|
80
|
+
|
|
81
|
+
## Human-Owned Debuggers
|
|
82
|
+
|
|
83
|
+
For debugger windows opened by a human, discover candidates with
|
|
84
|
+
`debug_candidate_search`, attach with `debug_candidate_attach`, and let the
|
|
85
|
+
debugger controller perform control actions. The visible debugger remains the
|
|
86
|
+
UI owner. Do not use general `image_evaluate` code to drive Morphic debugger
|
|
87
|
+
widgets or mutate contexts directly.
|
|
88
|
+
|
|
89
|
+
When the user asks you to perform a bounded action and give control back, run
|
|
90
|
+
only the requested debugger calls, then stop and report the resulting state.
|
|
91
|
+
|
|
92
|
+
## Reporting
|
|
93
|
+
|
|
94
|
+
When finishing a debugger-driven repair, report:
|
|
95
|
+
|
|
96
|
+
- repair action used;
|
|
97
|
+
- method created or recompiled, when any;
|
|
98
|
+
- critiques returned and whether they were accepted;
|
|
99
|
+
- proceed outcome;
|
|
100
|
+
- final state or why no fresh state is available;
|
|
101
|
+
- cleanup still needed.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Debugging Tools
|
|
2
|
+
|
|
3
|
+
Applies when using MCP-Pharo to capture exceptions, inspect debug sessions,
|
|
4
|
+
control paused execution, manage breakpoints, or repair code from a debugger
|
|
5
|
+
state.
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Use debugger tools without guessing from source, driving Morphic debugger UI by
|
|
10
|
+
hand, or issuing stale frame and variable references.
|
|
11
|
+
|
|
12
|
+
Debugger tools are discoverable, not part of the default static surface.
|
|
13
|
+
|
|
14
|
+
## Discovery Flow
|
|
15
|
+
|
|
16
|
+
1. Use `tool_search` with `group=debugging` to find available debugger
|
|
17
|
+
tools.
|
|
18
|
+
2. Use `tool_get` for the exact schema of the tool you plan to call.
|
|
19
|
+
3. If the current client exposes a catalog bridge, use `tool_call` to invoke
|
|
20
|
+
the selected debugger tool by name. Otherwise use the callable form exposed
|
|
21
|
+
by the current client.
|
|
22
|
+
|
|
23
|
+
## Must Do
|
|
24
|
+
|
|
25
|
+
- Treat `stateId`, `frameRef`, and `variableRef` values as opaque and
|
|
26
|
+
state-scoped.
|
|
27
|
+
- Use the newest returned state after every control or edit action.
|
|
28
|
+
- Keep stack and variable limits small first, then expand only what is needed.
|
|
29
|
+
- Clear temporary breakpoints and forget agent-owned sessions when the task is
|
|
30
|
+
done.
|
|
31
|
+
- Report the exact debugger tool names used and the final session state.
|
|
32
|
+
|
|
33
|
+
## Do Not
|
|
34
|
+
|
|
35
|
+
- Do not drive debugger windows or mutate contexts through general `image_evaluate`
|
|
36
|
+
code.
|
|
37
|
+
- Do not reuse frame or variable references after stepping, restarting,
|
|
38
|
+
resuming, terminating, or editing.
|
|
39
|
+
- Do not leave breakpoints or captured sessions behind without reporting them.
|
|
40
|
+
- Do not use debugger editing as a substitute for normal source tools when
|
|
41
|
+
there is no paused debugger state.
|
|
42
|
+
|
|
43
|
+
## Tool Map
|
|
44
|
+
|
|
45
|
+
| Tool | Use |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `debug_capture` | Evaluate a Smalltalk expression in a bounded worker process and capture runtime exceptions as tracked debug sessions. |
|
|
48
|
+
| `debug_test_run` | Run one SUnit test method in a bounded worker process and capture failures or errors as tracked debug sessions. |
|
|
49
|
+
| `debug_session_list` | List tracked debug sessions. |
|
|
50
|
+
| `debug_session_get` | Inspect one tracked debug session summary. |
|
|
51
|
+
| `debug_session_remove` | Remove a tracked debug session from the registry. |
|
|
52
|
+
| `debug_candidate_search` | Search unattached open debugger candidates. |
|
|
53
|
+
| `debug_candidate_attach` | Attach an open debugger candidate and return its debug state. |
|
|
54
|
+
| `debug_state_get` | Return the current session snapshot: stack, selected frame, source, scopes, repair actions, and state-scoped refs. |
|
|
55
|
+
| `debug_variable_get` | Expand scope and variable refs returned by `debug_state_get`. |
|
|
56
|
+
| `debug_expression_evaluate` | Evaluate a Smalltalk expression in a selected debug frame. |
|
|
57
|
+
| `debug_session_step_into` | Step into the selected frame of a tracked session. |
|
|
58
|
+
| `debug_session_step_over` | Step over the selected frame of a tracked session. |
|
|
59
|
+
| `debug_session_step_through` | Step through the selected frame of a tracked session. |
|
|
60
|
+
| `debug_session_restart` | Restart the selected frame of a tracked session. |
|
|
61
|
+
| `debug_session_resume` | Resume a tracked session. |
|
|
62
|
+
| `debug_session_terminate` | Terminate a tracked session. |
|
|
63
|
+
| `debug_breakpoint_list` | List tracked transient DebugPoint breakpoints. |
|
|
64
|
+
| `debug_breakpoint_set` | Install a transient method-entry or source-interval DebugPoint breakpoint. |
|
|
65
|
+
| `debug_breakpoint_remove` | Remove one tracked transient DebugPoint breakpoint. |
|
|
66
|
+
| `debug_breakpoint_enable` | Enable one tracked transient DebugPoint breakpoint. |
|
|
67
|
+
| `debug_breakpoint_disable` | Disable one tracked transient DebugPoint breakpoint. |
|
|
68
|
+
| `debug_breakpoint_clear` | Remove all tracked transient DebugPoint breakpoints. |
|
|
69
|
+
| `debug_method_update` | Update the method associated with a paused debug state, then proceed when possible. |
|
|
70
|
+
|
|
71
|
+
## Session Workflow
|
|
72
|
+
|
|
73
|
+
Use `debug_capture` when the agent should run code and capture its own
|
|
74
|
+
failure. If execution completes normally, there is no debugger session. If a
|
|
75
|
+
runtime exception is captured, use the returned `sessionId` and `state`.
|
|
76
|
+
|
|
77
|
+
Use `debug_test_run` when the failure is already expressed as one SUnit test method.
|
|
78
|
+
If the test passes, there is no debugger session. If it fails or errors, use the
|
|
79
|
+
returned `sessionId` and `state`.
|
|
80
|
+
|
|
81
|
+
Use `debug_candidate_search` when a human already opened a debugger. It returns
|
|
82
|
+
unattached candidates with identifying details. Attach by passing the
|
|
83
|
+
candidate's `candidateRef` to `debug_candidate_attach`.
|
|
84
|
+
|
|
85
|
+
Use `debug_session_remove` when you are done. Removing an agent-owned captured
|
|
86
|
+
session can terminate its worker process. Removing an attached human debugger
|
|
87
|
+
only removes it from the MCP registry.
|
|
88
|
+
|
|
89
|
+
## State And References
|
|
90
|
+
|
|
91
|
+
Treat `stateId`, `frameRef`, and `variableRef` values as state-scoped opaque
|
|
92
|
+
references. After a debug-session control tool or `debug_method_update`, old
|
|
93
|
+
frame and variable refs can be stale. Use the new returned state before
|
|
94
|
+
evaluating, expanding variables, or controlling execution again.
|
|
95
|
+
|
|
96
|
+
Use `debug_state_get` as the main context-loading tool. Keep stack and variable
|
|
97
|
+
limits small first, then expand details with `debug_variable_get` only when needed.
|
|
98
|
+
|
|
99
|
+
Use `debug_expression_evaluate` only for expressions that need the selected frame's
|
|
100
|
+
receiver or temporaries. Prefer normal code tools for project edits.
|
|
101
|
+
|
|
102
|
+
## Control
|
|
103
|
+
|
|
104
|
+
Use `debug_session_step_into`, `debug_session_step_over`,
|
|
105
|
+
`debug_session_step_through`, `debug_session_restart`,
|
|
106
|
+
`debug_session_resume`, and `debug_session_terminate` for control actions.
|
|
107
|
+
Control tools can return a `debug_state_get` snapshot when execution pauses.
|
|
108
|
+
Set `returnState=true` when the next step needs that snapshot inline;
|
|
109
|
+
otherwise call `debug_state_get` after the control action.
|
|
110
|
+
When the user asks for a bounded action, such as "step into twice", perform
|
|
111
|
+
those control calls and stop.
|
|
112
|
+
|
|
113
|
+
For externally attached debuggers, control should operate through the attached
|
|
114
|
+
debugger controller so the visible debugger remains the owner of its UI. Do not
|
|
115
|
+
manipulate debugger windows or contexts with general `image_evaluate` code.
|
|
116
|
+
|
|
117
|
+
## Breakpoints
|
|
118
|
+
|
|
119
|
+
Use the `debug_breakpoint_*` tools for temporary method-entry or
|
|
120
|
+
source-interval DebugPoint breakpoints. Clear breakpoints when the debugging
|
|
121
|
+
task ends.
|
|
122
|
+
|
|
123
|
+
File/line breakpoints are intentionally not emulated. Resolve source intervals
|
|
124
|
+
from the relevant method when source-interval precision is needed.
|
|
125
|
+
|
|
126
|
+
## Debugger-Driven Development
|
|
127
|
+
|
|
128
|
+
When a debug state offers repair actions, prefer `debug_method_update` over separate
|
|
129
|
+
manual edits. It can create missing methods or recompile stub/selected methods,
|
|
130
|
+
formats the compiled method, reports critiques, and proceeds when the edit is
|
|
131
|
+
accepted.
|
|
132
|
+
|
|
133
|
+
Read [Debugger-Driven Development](debugger-driven-development.md) before using
|
|
134
|
+
`debug_method_update` for missing methods, `subclassResponsibility`,
|
|
135
|
+
`shouldBeImplemented`, `notYetImplemented`, or iterative repair workflows.
|
|
136
|
+
|
|
137
|
+
## Reporting
|
|
138
|
+
|
|
139
|
+
When finishing debugger work, report:
|
|
140
|
+
|
|
141
|
+
- how the session was obtained: capture, test, discovered debugger, or attached
|
|
142
|
+
debugger;
|
|
143
|
+
- debugger tools called;
|
|
144
|
+
- final session outcome: completed, paused, running, terminated, forgotten, or
|
|
145
|
+
still open;
|
|
146
|
+
- breakpoints created, cleared, or left in place;
|
|
147
|
+
- cleanup performed or still needed.
|