@evref-bl/pharo-mcp 3.0.0 → 3.2.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 +5 -7
- package/package.json +1 -1
- package/templates/pharo-references/pharo-coding.md +105 -0
- package/templates/pharo-references/tool-reference.md +78 -158
- package/templates/pharo-skills/pharo-ci-repro/references/pharo-smalltalkci.md +4 -28
- package/templates/pharo-skills/pharo-code-profiling/SKILL.md +19 -38
- package/templates/pharo-skills/pharo-image-sync/SKILL.md +47 -0
- package/templates/pharo-skills/pharo-image-sync/agents/openai.yaml +4 -0
- package/templates/pharo-skills/pharo-mcp-recovery/SKILL.md +1 -1
- package/templates/pharo-skills/pharo-project-load/SKILL.md +2 -1
- package/templates/pharo.AGENTS.md +16 -14
- package/templates/pharo-references/building-on-mcp.md +0 -473
- package/templates/pharo-references/debugging.md +0 -70
- package/templates/pharo-references/getting-started.md +0 -151
- package/templates/pharo-references/pharo-coding-rules.md +0 -147
- package/templates/pharo-references/pharo-coding-style.md +0 -141
- package/templates/pharo-references/safety-and-ecosystem.md +0 -323
- package/templates/pharo-references/source-vs-live-image.md +0 -75
- package/templates/pharo-references/troubleshooting.md +0 -145
- package/templates/pharo-references/using-pharo-mcp.md +0 -86
- package/templates/pharo-skills/pharo-image-git-handoff/SKILL.md +0 -40
- package/templates/pharo-skills/pharo-image-git-handoff/agents/openai.yaml +0 -4
- package/templates/pharo-skills/pharo-version-compat/SKILL.md +0 -54
- package/templates/pharo-skills/pharo-version-compat/agents/openai.yaml +0 -4
|
@@ -1,38 +1,40 @@
|
|
|
1
1
|
# Pharo MCP Agent Guide
|
|
2
2
|
|
|
3
3
|
Use the configured `pharo` MCP server whenever a task depends on the running
|
|
4
|
-
image. Preserve unrelated changes and
|
|
5
|
-
|
|
4
|
+
image. Preserve unrelated changes, and say which live check you could not run
|
|
5
|
+
rather than letting source stand in for it.
|
|
6
6
|
|
|
7
7
|
## Read First
|
|
8
8
|
|
|
9
9
|
- Inspect the tools and schemas exposed by the current client; do not rely on
|
|
10
|
-
remembered names.
|
|
10
|
+
remembered names. `tool_search` finds what is not advertised, and `tool_get`
|
|
11
|
+
answers one tool's schema.
|
|
11
12
|
- Prefer the least-mutating dedicated tool. Use `image_evaluate` only when no
|
|
12
13
|
dedicated tool fits.
|
|
13
|
-
- Treat image, Tonel, Iceberg, and Git state as separate.
|
|
14
|
+
- Treat image, Tonel, Iceberg, and Git state as separate. None of them proves
|
|
15
|
+
anything about the others.
|
|
16
|
+
- The image is one workspace. It has no branches and no worktrees, so two
|
|
17
|
+
agents working in it at once share every edit, and one export writes both
|
|
18
|
+
their changes into one diff. One writer at a time.
|
|
14
19
|
- Tools change the running image but never save it; use a safe image boundary.
|
|
15
20
|
|
|
16
21
|
## Context Loading
|
|
17
22
|
|
|
18
23
|
- Before Pharo source changes: read the project's Pharo coding and testing
|
|
19
24
|
guidance. If none exists, use the local
|
|
20
|
-
[coding
|
|
21
|
-
|
|
25
|
+
[Pharo coding](pharo-references/pharo-coding.md) reference.
|
|
26
|
+
- Before reading an answer closely, rewriting methods, or acting on a warning a
|
|
27
|
+
call came back with: read
|
|
28
|
+
[tool reference](pharo-references/tool-reference.md).
|
|
22
29
|
- Before loading a project: use `skills/pharo-project-load/SKILL.md`.
|
|
23
|
-
- Before
|
|
24
|
-
-
|
|
25
|
-
`skills/pharo-image-git-handoff/SKILL.md`.
|
|
30
|
+
- Before moving code between the image and its files: use
|
|
31
|
+
`skills/pharo-image-sync/SKILL.md`.
|
|
26
32
|
- Before reproducing smalltalkCI or GitHub failures: use
|
|
27
33
|
`skills/pharo-ci-repro/SKILL.md`.
|
|
28
34
|
- Before profiling or optimizing Pharo code: use
|
|
29
35
|
`skills/pharo-code-profiling/SKILL.md`.
|
|
30
36
|
- Before recovering a stopped standalone image: use
|
|
31
37
|
`skills/pharo-mcp-recovery/SKILL.md`.
|
|
32
|
-
- Before debugger sessions or repairs: read the local
|
|
33
|
-
[debugging guide](pharo-references/debugging.md).
|
|
34
|
-
- Before giving a project MCP tools, resources or prompts of its own: read
|
|
35
|
-
[building on MCP](pharo-references/building-on-mcp.md).
|
|
36
38
|
|
|
37
39
|
## Workflow
|
|
38
40
|
|
|
@@ -40,7 +42,7 @@ guessing from source.
|
|
|
40
42
|
2. Preview or verify identity before risky mutation.
|
|
41
43
|
3. Make the narrowest image-side change.
|
|
42
44
|
4. Run focused SUnit tests and inspect the result.
|
|
43
|
-
5. Use the
|
|
45
|
+
5. Use the image-sync skill before exporting or committing Tonel changes.
|
|
44
46
|
|
|
45
47
|
For repository work, verify repository identity before mutation and inspect the
|
|
46
48
|
image-side diff before export or commit.
|
|
@@ -1,473 +0,0 @@
|
|
|
1
|
-
# Building On MCP
|
|
2
|
-
|
|
3
|
-
MCP is a server you can embed. Loading `Core` gives you the Streamable HTTP
|
|
4
|
-
transport, the JSON-RPC and schema machinery and the catalog tools, and
|
|
5
|
-
nothing that knows about Pharo code. Everything the server then offers a
|
|
6
|
-
client — tools, resources, prompts — you write in a package of your own.
|
|
7
|
-
|
|
8
|
-
A project doing this changes nothing in MCP. Its tools live in a package of
|
|
9
|
-
its own, which it loads when it wants them served and leaves out when it does
|
|
10
|
-
not.
|
|
11
|
-
|
|
12
|
-
## Load the server alone
|
|
13
|
-
|
|
14
|
-
Depend on the baseline and ask for `Core`:
|
|
15
|
-
|
|
16
|
-
```smalltalk
|
|
17
|
-
BaselineOfMyProject >> baseline: spec
|
|
18
|
-
|
|
19
|
-
<baseline>
|
|
20
|
-
spec for: #common do: [
|
|
21
|
-
spec baseline: 'MCP' with: [
|
|
22
|
-
spec
|
|
23
|
-
repository: 'github://Evref-BL/MCP:vX.Y.Z/src'; "a release, not a branch"
|
|
24
|
-
loads: #( 'Core' ) ].
|
|
25
|
-
spec package: 'MyProject-MCP' with: [ spec requires: #( 'MyProject' 'MCP' ) ].
|
|
26
|
-
spec group: 'Tools' with: #( 'MyProject-MCP' ).
|
|
27
|
-
spec group: 'default' with: #( 'Core' 'Tests' ) ]
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Two things in it are worth copying. The group is named `Tools` rather than
|
|
31
|
-
`MCP`, because Metacello refuses a group and a baseline that share one name.
|
|
32
|
-
And it is left out of `default`: your project is useful without a server, and
|
|
33
|
-
an image that does not serve MCP has no use for tools it cannot register.
|
|
34
|
-
|
|
35
|
-
The other groups are `IDE`, MCP's own image tools for classes, methods,
|
|
36
|
-
packages, tests, repositories, change history and debugging; `UI`,
|
|
37
|
-
the world-menu server list; `Telemetry`, the OpenTelemetry backend; and
|
|
38
|
-
`default`, which is `Core`, `IDE`, `UI` and their tests.
|
|
39
|
-
|
|
40
|
-
## The three things a server offers
|
|
41
|
-
|
|
42
|
-
| Kind | What it is | Who reaches for it | Subclass of | Names itself with |
|
|
43
|
-
| --- | --- | --- | --- | --- |
|
|
44
|
-
| Tool | an action with arguments | the model, on its own | `MCPTool` | `toolName` |
|
|
45
|
-
| Resource | a document to read | the host, for the user | `MCPResource` | `resourceUri` |
|
|
46
|
-
| Prompt | a message template | the user, deliberately | `MCPPrompt` | `promptName` |
|
|
47
|
-
|
|
48
|
-
They are independent. A server may have one of them, two, or all three.
|
|
49
|
-
|
|
50
|
-
## A tool
|
|
51
|
-
|
|
52
|
-
Give your tools a common superclass. It is where the things they share live —
|
|
53
|
-
the group they belong to, the icon they are drawn with, the helpers that talk
|
|
54
|
-
to your project:
|
|
55
|
-
|
|
56
|
-
```smalltalk
|
|
57
|
-
MCPTool << #MyProjectTool
|
|
58
|
-
package: 'MyProject-MCP'
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
```smalltalk
|
|
62
|
-
MyProjectTool class >> groupName
|
|
63
|
-
|
|
64
|
-
^ 'widgets'
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
The base is not served, and needs to say nothing to stay out: it answers no
|
|
68
|
-
`toolName`, so there is no tool there to register.
|
|
69
|
-
|
|
70
|
-
Then one class per tool:
|
|
71
|
-
|
|
72
|
-
```smalltalk
|
|
73
|
-
MyProjectTool << #MyProjectToolListWidgets
|
|
74
|
-
package: 'MyProject-MCP'
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
```smalltalk
|
|
78
|
-
MyProjectToolListWidgets class >> toolName
|
|
79
|
-
|
|
80
|
-
^ 'widget_list'
|
|
81
|
-
|
|
82
|
-
MyProjectToolListWidgets >> title
|
|
83
|
-
|
|
84
|
-
^ 'List Widgets'
|
|
85
|
-
|
|
86
|
-
MyProjectToolListWidgets >> description
|
|
87
|
-
|
|
88
|
-
^ 'List the widgets this image holds, optionally those whose name starts with a prefix. Use widget_get to read one.'
|
|
89
|
-
|
|
90
|
-
MyProjectToolListWidgets >> additionalKeywords
|
|
91
|
-
|
|
92
|
-
^ #( 'widget' 'inventory' )
|
|
93
|
-
|
|
94
|
-
MyProjectToolListWidgets >> annotations
|
|
95
|
-
|
|
96
|
-
^ MCPStructureToolAnnotations readOnly
|
|
97
|
-
|
|
98
|
-
MyProjectToolListWidgets >> buildInputSchema
|
|
99
|
-
|
|
100
|
-
^ MCPStructureInputSchema new
|
|
101
|
-
type: 'object';
|
|
102
|
-
properties: { (self
|
|
103
|
-
schemaPropertyNamed: 'prefix'
|
|
104
|
-
type: 'string'
|
|
105
|
-
description: 'Only answer widgets whose name starts with this. Defaults to all of them.') };
|
|
106
|
-
required: #( );
|
|
107
|
-
yourself
|
|
108
|
-
|
|
109
|
-
MyProjectToolListWidgets >> executeWithRequest: request
|
|
110
|
-
|
|
111
|
-
| prefix names |
|
|
112
|
-
prefix := request stringArgumentNamed: 'prefix' default: ''.
|
|
113
|
-
names := (MyProject widgetNames select: [ :each | each beginsWith: prefix ]) asArray sorted.
|
|
114
|
-
^ self
|
|
115
|
-
successResultText: names size asString , ' widget(s): ' , (', ' join: names) , '.'
|
|
116
|
-
data: { (#widgets -> names) } asDictionary
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
`MyProject widgetNames` stands for your own code. What the rest is for:
|
|
120
|
-
|
|
121
|
-
- **`toolName`** is the key in one catalog shared with every other tool the
|
|
122
|
-
image has loaded, so pick a first word that is yours. `widget_list` is
|
|
123
|
-
safe; `class_search` is taken.
|
|
124
|
-
- **`description`** is read by a model deciding whether to call you. Say what
|
|
125
|
-
the tool answers and name the tools that come next, the way the example
|
|
126
|
-
points at `widget_get`.
|
|
127
|
-
- **`additionalKeywords`** are the words `tool_search` matches besides the
|
|
128
|
-
ones already in the name and title.
|
|
129
|
-
- **`annotations`** tell a client what the tool does to the image.
|
|
130
|
-
`MCPStructureToolAnnotations readOnly` promises it changes nothing;
|
|
131
|
-
`mayModify` says otherwise. A host may require a confirmation for the
|
|
132
|
-
second.
|
|
133
|
-
- **`buildInputSchema`** is the contract the server validates an incoming
|
|
134
|
-
call against before your code runs. `additionalProperties` defaults to
|
|
135
|
-
false, so a caller that misspells an argument is told so instead of having
|
|
136
|
-
it ignored. Every property takes a description; it is the only place the
|
|
137
|
-
caller learns what the argument means.
|
|
138
|
-
|
|
139
|
-
## Being served
|
|
140
|
-
|
|
141
|
-
A tool is served because its package was loaded, and naming itself is how a
|
|
142
|
-
class says it is a tool. Nothing else is needed: load `MyProject-MCP` and
|
|
143
|
-
every server in the image has `widget_list`.
|
|
144
|
-
|
|
145
|
-
It is the tool's **own** name that serves it, not an inherited one. A
|
|
146
|
-
subclass of a tool is a variation of that tool, so specialising one does not
|
|
147
|
-
claim the name it already holds — give the subclass its own `toolName` when
|
|
148
|
-
it is a tool of its own.
|
|
149
|
-
|
|
150
|
-
A class that must stay out of every catalog says so itself:
|
|
151
|
-
|
|
152
|
-
```smalltalk
|
|
153
|
-
MyProjectTestTool class >> shouldAutoRegister
|
|
154
|
-
|
|
155
|
-
^ false
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
That is the right thing for a test fixture, which would otherwise be served
|
|
159
|
-
by every server in the image whose tests are loaded.
|
|
160
|
-
|
|
161
|
-
## Reading arguments
|
|
162
|
-
|
|
163
|
-
`executeWithRequest:` receives an `MCPToolRequest` whose arguments have
|
|
164
|
-
already been validated against your schema. Read them with the typed
|
|
165
|
-
accessors rather than by hand, so a missing or oddly typed value fails in one
|
|
166
|
-
place:
|
|
167
|
-
|
|
168
|
-
```smalltalk
|
|
169
|
-
request stringArgumentNamed: 'name'.
|
|
170
|
-
request stringArgumentNamed: 'name' default: 'anything'.
|
|
171
|
-
request stringArgumentNamed: 'side' in: #( 'instance' 'class' ) default: 'instance'.
|
|
172
|
-
request booleanArgumentNamed: 'force' default: false.
|
|
173
|
-
request nonNegativeIntegerArgumentNamed: 'offset' default: 0.
|
|
174
|
-
request stringCollectionArgumentNamed: 'packages' default: #( ).
|
|
175
|
-
request hasArgumentNamed: 'prefix'.
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
## Answering
|
|
179
|
-
|
|
180
|
-
Two answers, both built by the tool:
|
|
181
|
-
|
|
182
|
-
```smalltalk
|
|
183
|
-
self successResultText: 'Three widgets.' data: { (#widgets -> names) } asDictionary.
|
|
184
|
-
self errorResultText: 'No widget named ' , name , '.' details: { (#known -> names) } asDictionary
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
The text is what a model reads; the data is the same answer in a shape a
|
|
188
|
-
program can use. Give both. A text that says only "Done." costs the caller
|
|
189
|
-
another round trip to find out what happened.
|
|
190
|
-
|
|
191
|
-
You do not have to catch to report a failure. An exception that escapes
|
|
192
|
-
`executeWithRequest:` is answered as an error result naming your tool, the
|
|
193
|
-
error class, the message and the frames of your own code; the server leaves
|
|
194
|
-
its own frames out. Build an error result yourself when you know something
|
|
195
|
-
the exception does not, such as what the caller should have asked for.
|
|
196
|
-
|
|
197
|
-
Which of the two reaches the client is the server's choice, not the tool's.
|
|
198
|
-
`toolResponseFields` defaults to `#content`, where the data is rendered into
|
|
199
|
-
the text. `useContentAndStructuredContentToolResponses` sends both, and
|
|
200
|
-
`useStructuredContentOnlyToolResponses` sends only the structured half. The
|
|
201
|
-
structured half carries your text as `summary` and your dictionary as `data`.
|
|
202
|
-
|
|
203
|
-
### Answering a page
|
|
204
|
-
|
|
205
|
-
A tool that answers a list answers one page of it, so a long result never
|
|
206
|
-
arrives as one block a model has to read through. Three parts: the shared
|
|
207
|
-
`offset` argument, how big a page is, and the slicing. The listing tool
|
|
208
|
-
again, now that the list can be long:
|
|
209
|
-
|
|
210
|
-
```smalltalk
|
|
211
|
-
MyProjectToolListWidgets >> buildInputSchema
|
|
212
|
-
|
|
213
|
-
^ MCPStructureInputSchema new
|
|
214
|
-
type: 'object';
|
|
215
|
-
properties: { self offsetInputProperty };
|
|
216
|
-
required: #( );
|
|
217
|
-
yourself
|
|
218
|
-
|
|
219
|
-
MyProjectToolListWidgets >> defaultPageLimit
|
|
220
|
-
|
|
221
|
-
^ 25
|
|
222
|
-
|
|
223
|
-
MyProjectToolListWidgets >> executeWithRequest: request
|
|
224
|
-
|
|
225
|
-
| page |
|
|
226
|
-
page := MCPPaginationResult
|
|
227
|
-
fromEntries: MyProject widgetNames asArray sorted
|
|
228
|
-
limit: self defaultPageLimit
|
|
229
|
-
offset: (request nonNegativeIntegerArgumentNamed: 'offset' default: 0).
|
|
230
|
-
^ self
|
|
231
|
-
successResultText: page returnedCount asString , ' widget(s).'
|
|
232
|
-
data: (page asDictionaryWithEntriesKey: #widgets)
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
`offsetInputProperty` is the same `offset` argument every paging tool takes,
|
|
236
|
-
declared once so they all describe it the same way. `MCPPaginationResult`
|
|
237
|
-
cuts the page and answers the shape MCP's own tools answer: your entries
|
|
238
|
-
under the key you name, a `pagination` summary beside them, and `nextOffset`
|
|
239
|
-
while entries remain — a caller asks again with that offset until it stops
|
|
240
|
-
coming.
|
|
241
|
-
|
|
242
|
-
Answering `defaultPageLimit` is what puts `pageSize` in your contract, so
|
|
243
|
-
`tool_get` can tell a caller how large a page is before asking for one. A
|
|
244
|
-
tool that answers a whole result rather than pages leaves it alone.
|
|
245
|
-
|
|
246
|
-
## Groups and icons
|
|
247
|
-
|
|
248
|
-
`groupName` is how the catalog and the world-menu browser group related
|
|
249
|
-
tools. Pick one for your project and answer it on the shared superclass.
|
|
250
|
-
|
|
251
|
-
MCP reads an icon out of a tool name whose words it recognises — `class`,
|
|
252
|
-
`method`, `search`, `create` — which is how its own tools stay consistent
|
|
253
|
-
without anyone choosing. Your words are not in that vocabulary, so declare an
|
|
254
|
-
icon:
|
|
255
|
-
|
|
256
|
-
```smalltalk
|
|
257
|
-
MyProjectTool class >> toolIconSpec
|
|
258
|
-
|
|
259
|
-
^ MCPToolIconSpec baseIconNamed: #pharoHalfScale badgeIconNamed: #browse
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
## A resource
|
|
263
|
-
|
|
264
|
-
A resource is a document the host can put in front of the model without the
|
|
265
|
-
model asking. Where a tool answers a question, a resource is material:
|
|
266
|
-
|
|
267
|
-
```smalltalk
|
|
268
|
-
MCPResource << #MyProjectWidgetResource
|
|
269
|
-
package: 'MyProject-MCP'
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
```smalltalk
|
|
273
|
-
MyProjectWidgetResource class >> resourceUri
|
|
274
|
-
|
|
275
|
-
^ 'myproject://widgets'
|
|
276
|
-
|
|
277
|
-
MyProjectWidgetResource class >> resourceName
|
|
278
|
-
|
|
279
|
-
^ 'widgets'
|
|
280
|
-
|
|
281
|
-
MyProjectWidgetResource >> title
|
|
282
|
-
|
|
283
|
-
^ 'Widget Inventory'
|
|
284
|
-
|
|
285
|
-
MyProjectWidgetResource >> description
|
|
286
|
-
|
|
287
|
-
^ 'Every widget this image holds, as JSON.'
|
|
288
|
-
|
|
289
|
-
MyProjectWidgetResource >> mimeType
|
|
290
|
-
|
|
291
|
-
^ 'application/json'
|
|
292
|
-
|
|
293
|
-
MyProjectWidgetResource >> readContents
|
|
294
|
-
|
|
295
|
-
^ self textContents: (STONJSON toStringPretty: { (#widgets -> MyProject widgetNames) } asDictionary)
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
The URI is the key in the registry, and the same rule applies: a resource
|
|
299
|
-
that names itself is served.
|
|
300
|
-
|
|
301
|
-
### A family of resources
|
|
302
|
-
|
|
303
|
-
When the document is one of many, named by something in the URI, subclass
|
|
304
|
-
`MCPResourceTemplate`. It names itself with a `resourceUriTemplate` in RFC
|
|
305
|
-
6570 form, resolves one URI at a time through `resourceForUri:`, and may
|
|
306
|
-
suggest values for its variables:
|
|
307
|
-
|
|
308
|
-
```smalltalk
|
|
309
|
-
MyProjectWidgetTemplate class >> resourceUriTemplate
|
|
310
|
-
|
|
311
|
-
^ 'myproject://widget/{name}'
|
|
312
|
-
|
|
313
|
-
MyProjectWidgetTemplate >> resourceForUri: aUri
|
|
314
|
-
|
|
315
|
-
| name |
|
|
316
|
-
(aUri beginsWith: MyProjectOneWidgetResource resourceUri) ifFalse: [ ^ nil ].
|
|
317
|
-
name := aUri allButFirst: MyProjectOneWidgetResource resourceUri size.
|
|
318
|
-
^ name
|
|
319
|
-
ifEmpty: [ nil ]
|
|
320
|
-
ifNotEmpty: [ MyProjectOneWidgetResource named: name ]
|
|
321
|
-
|
|
322
|
-
MyProjectWidgetTemplate >> completionsFor: aVariableName value: aPrefix context: aContextDictionary
|
|
323
|
-
|
|
324
|
-
aVariableName = 'name' ifFalse: [ ^ #( ) ].
|
|
325
|
-
^ MyProject widgetNames select: [ :each | each beginsWith: aPrefix ]
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
A template lists through `resources/templates/list` and never answers
|
|
329
|
-
contents of its own.
|
|
330
|
-
|
|
331
|
-
What it resolves to is an ordinary resource built per URI rather than
|
|
332
|
-
registered. It carries the name it was built with, answers its own `uri`, and
|
|
333
|
-
keeps itself out of the catalog, where one entry standing for the whole family
|
|
334
|
-
would be wrong:
|
|
335
|
-
|
|
336
|
-
```smalltalk
|
|
337
|
-
MyProjectOneWidgetResource class >> resourceUri
|
|
338
|
-
|
|
339
|
-
^ 'myproject://widget/'
|
|
340
|
-
|
|
341
|
-
MyProjectOneWidgetResource class >> shouldAutoRegister
|
|
342
|
-
|
|
343
|
-
^ false
|
|
344
|
-
|
|
345
|
-
MyProjectOneWidgetResource class >> named: aName
|
|
346
|
-
|
|
347
|
-
^ self new
|
|
348
|
-
widgetName: aName;
|
|
349
|
-
yourself
|
|
350
|
-
|
|
351
|
-
MyProjectOneWidgetResource >> uri
|
|
352
|
-
|
|
353
|
-
^ self class resourceUri , self widgetName
|
|
354
|
-
|
|
355
|
-
MyProjectOneWidgetResource >> name
|
|
356
|
-
|
|
357
|
-
^ self widgetName
|
|
358
|
-
|
|
359
|
-
MyProjectOneWidgetResource >> readContents
|
|
360
|
-
|
|
361
|
-
^ self textContents: (MyProject describeWidgetNamed: self widgetName)
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
## A prompt
|
|
365
|
-
|
|
366
|
-
A prompt is user-controlled where a tool is model-controlled: a client
|
|
367
|
-
usually shows them as slash commands, so a prompt is something a person
|
|
368
|
-
reaches for deliberately.
|
|
369
|
-
|
|
370
|
-
```smalltalk
|
|
371
|
-
MyProjectReviewPrompt class >> promptName
|
|
372
|
-
|
|
373
|
-
^ 'review_widget'
|
|
374
|
-
|
|
375
|
-
MyProjectReviewPrompt >> title
|
|
376
|
-
|
|
377
|
-
^ 'Review A Widget'
|
|
378
|
-
|
|
379
|
-
MyProjectReviewPrompt >> description
|
|
380
|
-
|
|
381
|
-
^ 'Asks for a review of one widget.'
|
|
382
|
-
|
|
383
|
-
MyProjectReviewPrompt >> arguments
|
|
384
|
-
|
|
385
|
-
^ { (MCPStructurePromptArgument name: 'widget' description: 'The widget to review.' required: true) }
|
|
386
|
-
|
|
387
|
-
MyProjectReviewPrompt >> messagesFor: anArgumentDictionary
|
|
388
|
-
|
|
389
|
-
^ self userMessage: 'Review the widget named ' , (anArgumentDictionary at: 'widget')
|
|
390
|
-
, ' and say what you would change.'
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
Like a template, a prompt may answer `completionsFor:value:context:` for its
|
|
394
|
-
own arguments.
|
|
395
|
-
|
|
396
|
-
## Shaping the server's surface
|
|
397
|
-
|
|
398
|
-
Two words govern it. A **registered** tool is in the catalog: callable by
|
|
399
|
-
name, and findable with `tool_search` and `tool_get`. An **advertised** tool
|
|
400
|
-
is also in `tools/list`, which is the list a client shows a model up front.
|
|
401
|
-
Advertising decides what is shown, not what may be called.
|
|
402
|
-
|
|
403
|
-
A tool from outside MCP is registered and discoverable, not advertised, so it
|
|
404
|
-
costs nothing in the client's context until something looks for it. Answer
|
|
405
|
-
`'static'` from `defaultExposure` to be in `tools/list` from the start.
|
|
406
|
-
|
|
407
|
-
Against that default, a server is configured directly:
|
|
408
|
-
|
|
409
|
-
```smalltalk
|
|
410
|
-
mcp := MCP new.
|
|
411
|
-
mcp addTool: MyProjectToolListWidgets. "or addTools: { A. B }"
|
|
412
|
-
mcp removeTool: 'image_evaluate'.
|
|
413
|
-
mcp advertiseTool: MyProjectToolListWidgets. "adds one to what is advertised"
|
|
414
|
-
mcp advertiseTools: { MyProjectToolListWidgets }. "replaces the advertised set"
|
|
415
|
-
mcp advertiseAllTools.
|
|
416
|
-
mcp advertiseToolDefaults.
|
|
417
|
-
mcp port: 4000.
|
|
418
|
-
mcp start.
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
`MCP startOnPort:` on the class side answers the server already on that port,
|
|
422
|
-
or builds one with the default catalog. A server you configured is started
|
|
423
|
-
through the instance, as above.
|
|
424
|
-
|
|
425
|
-
For a server holding only your own tools:
|
|
426
|
-
|
|
427
|
-
```smalltalk
|
|
428
|
-
mcp := MCP onlyTools: { MyProjectToolListWidgets. MyProjectToolGetWidget }.
|
|
429
|
-
mcp port: 4000.
|
|
430
|
-
mcp start.
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
`onlyTools:` replaces the catalog with exactly those, advertises all of them,
|
|
434
|
-
and discovers nothing afterwards. That includes the catalog tools: a server
|
|
435
|
-
built this way has no `tool_search`, `tool_get` or `tool_call` unless you add
|
|
436
|
-
them back. Add `MCPToolSearchTools`, `MCPToolGetTool` and `MCPToolCallTool`
|
|
437
|
-
when you want a surface a model can explore, or build up from `MCP new` with
|
|
438
|
-
`addTool:` instead.
|
|
439
|
-
|
|
440
|
-
## Testing what you serve
|
|
441
|
-
|
|
442
|
-
A server answers its JSON-RPC methods as plain messages, so a test needs no
|
|
443
|
-
socket and no port:
|
|
444
|
-
|
|
445
|
-
```smalltalk
|
|
446
|
-
MyProjectToolTest >> testListingAnswersEveryWidget
|
|
447
|
-
|
|
448
|
-
| result |
|
|
449
|
-
result := (MCP new useContentAndStructuredContentToolResponses; yourself)
|
|
450
|
-
rpcToolCall: 'widget_list' withParams: Dictionary new.
|
|
451
|
-
self assert: (((result at: #structuredContent) at: #data) at: #widgets) equals: #( 'alpha' 'beta' 'gamma' ).
|
|
452
|
-
self assert: (((result at: #content) first at: #text) beginsWith: '3 widget(s)')
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
Assert on the data rather than on the whole text: under the default
|
|
456
|
-
`#content`, the text a client receives is your summary with the data rendered
|
|
457
|
-
underneath it, so an equality test on it breaks whenever either half changes.
|
|
458
|
-
|
|
459
|
-
`rpcResourceRead:`, `rpcPromptGet:arguments:`, `rpcToolsList`,
|
|
460
|
-
`rpcResourcesList` and `rpcPromptsList` are there for the same purpose.
|
|
461
|
-
|
|
462
|
-
## Things that bite
|
|
463
|
-
|
|
464
|
-
- **A name already taken.** The catalog holds one tool per name. Prefix with
|
|
465
|
-
a word that is yours.
|
|
466
|
-
- **A subclass of an existing tool is not served** until it names itself.
|
|
467
|
-
That is deliberate: it would otherwise claim its superclass's name and the
|
|
468
|
-
two would race for the key.
|
|
469
|
-
- **A fixture in a loaded test package is served** like anything else.
|
|
470
|
-
Answer `false` from `shouldAutoRegister`.
|
|
471
|
-
- **`onlyTools:` drops the catalog tools.** See above.
|
|
472
|
-
- **Nothing is saved.** Tools change the running image and never write it to
|
|
473
|
-
disk. `Smalltalk saveSession` is the caller's business, not the tool's.
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Debugging Tools
|
|
2
|
-
|
|
3
|
-
Applies when using MCP-Pharo to watch values while code runs, or to see what
|
|
4
|
-
debuggers a person has open.
|
|
5
|
-
|
|
6
|
-
## Goal
|
|
7
|
-
|
|
8
|
-
See what running code is doing without guessing from source and without driving
|
|
9
|
-
the Morphic debugger by hand.
|
|
10
|
-
|
|
11
|
-
Debugger tools are discoverable, not part of the default static surface.
|
|
12
|
-
|
|
13
|
-
## Discovery Flow
|
|
14
|
-
|
|
15
|
-
1. Use `tool_search` with a query such as `watch` or `debugger`.
|
|
16
|
-
2. Use `tool_get` for the exact schema of the tool you plan to call.
|
|
17
|
-
3. If the current client exposes a catalog bridge, use `tool_call` to invoke
|
|
18
|
-
the selected tool by name. Otherwise use the callable form exposed by the
|
|
19
|
-
current client.
|
|
20
|
-
|
|
21
|
-
## Tool Map
|
|
22
|
-
|
|
23
|
-
| Tool | Use |
|
|
24
|
-
| --- | --- |
|
|
25
|
-
| `debug_watch` | Record what variables hold while an expression runs, without stopping it. |
|
|
26
|
-
| `debug_search` | Answer the debuggers open in the image, read-only. |
|
|
27
|
-
|
|
28
|
-
Neither tool keeps anything. No session is started, attached or registered, so
|
|
29
|
-
there is no reference to carry between calls and nothing to clean up.
|
|
30
|
-
|
|
31
|
-
## Watching Values
|
|
32
|
-
|
|
33
|
-
`debug_watch` is print debugging without editing the method. Name the methods to
|
|
34
|
-
probe, the variables to record, and the expression to run. Every assignment to a
|
|
35
|
-
named variable is recorded as it happens, the expression runs to completion, and
|
|
36
|
-
the probes are removed before the call answers.
|
|
37
|
-
|
|
38
|
-
Name no variable and each method's arguments are recorded as it is entered.
|
|
39
|
-
Both `method` and `variable` take one value or a list, and a probe goes on every
|
|
40
|
-
pair that exists, so a variable a method never assigns simply has no series there.
|
|
41
|
-
|
|
42
|
-
Reach for it whenever the question is what a value became, rather than where to
|
|
43
|
-
intervene.
|
|
44
|
-
|
|
45
|
-
## Seeing A Person's Debuggers
|
|
46
|
-
|
|
47
|
-
`debug_search` answers the debuggers open in the image: each one's name, the
|
|
48
|
-
exception it stopped on, and the frames nearest the stop with what those frames
|
|
49
|
-
hold. Pass a `query` to keep only the debuggers whose name or exception matches.
|
|
50
|
-
|
|
51
|
-
It is a query and nothing else. It attaches to nothing, changes nothing, and
|
|
52
|
-
remembers nothing, so the picture it gives is whatever is open at that moment.
|
|
53
|
-
Ask again for a fresh one.
|
|
54
|
-
|
|
55
|
-
Act on what it shows with the ordinary tools: read the method with `method_get`,
|
|
56
|
-
change it with `method_compile`, run something with `image_evaluate`. Stepping,
|
|
57
|
-
resuming and repairing from inside a frame belong to the person at the debugger,
|
|
58
|
-
not to the agent.
|
|
59
|
-
|
|
60
|
-
## To Debug Your Own Code
|
|
61
|
-
|
|
62
|
-
Do not open a debugger for it. Call the tool that runs the code and read the
|
|
63
|
-
failure it reports: it names the frames nearest the error and the values they
|
|
64
|
-
held. When that is not enough, `debug_watch` shows how a value got there.
|
|
65
|
-
|
|
66
|
-
## Do Not
|
|
67
|
-
|
|
68
|
-
- Do not drive debugger windows or mutate contexts through general
|
|
69
|
-
`image_evaluate` code.
|
|
70
|
-
- Do not resume, step or terminate a person's debugger. It is theirs.
|