1- 'From Cuis7.7 [latest update: #7777] on 20 January 2026 at 5:17:14 pm'!
1+ 'From Cuis7.7 [latest update: #7777] on 21 January 2026 at 6:20:31 pm'!
22'Description MCP Server for Claude Code integration.
33
44Implements the Model Context Protocol (MCP) over stdio with JSON-RPC 2.0.
55Uses JSON Lines protocol (one JSON message per line, no Content-Length headers).
66Provides 12 tools for Smalltalk interaction (saveImage intentionally excluded).
77
8+ REQUIRES: OSProcess package for async stdio support (GUI responsive during MCP wait).
9+ Uses BufferedAsyncFileReadStream which provides semaphore-based waiting,
10+ allowing the VM scheduler to run other processes (including the UI event loop)
11+ while the MCP server waits for input from Claude Code.
12+
813Usage:
9- 1. Load this package into a Cuis image
10- 2. Save the image
11- 3. Run with --mcp flag:
14+ 1. Load JSON and OSProcess packages into a Cuis image
15+ 2. Load this package (MCP-Server)
16+ 3. Save the image
17+ 4. Run with --mcp flag:
1218 squeak myImage.image --mcp
1319
1420Claude Code config (~/.claude.json):
@@ -22,7 +28,9 @@ Claude Code config (~/.claude.json):
2228 }
2329 }
2430'!
25- !provides: 'MCP-Server' 1 4!
31+ !provides: 'MCP-Server' 1 11!
32+ !requires: 'JSON' nil nil nil!
33+ !requires: 'OSProcess' nil nil nil!
2634SystemOrganization addCategory: #'MCP-Server'!
2735
2836
@@ -47,24 +55,22 @@ MCPTransport class
4755 instanceVariableNames: ''!
4856
4957
50- !MCPServer methodsFor: 'initialization' stamp: ''!
58+ !MCPServer methodsFor: 'initialization' stamp: 'jmm 1/26/2026 11:00 '!
5159initialize
5260 transport := MCPTransport new.
5361 running := false.! !
5462
55- !MCPServer methodsFor: 'running' stamp: ''!
63+ !MCPServer methodsFor: 'running' stamp: 'jmm 1/26/2026 11:00 '!
5664run
57- | temp1 temp2 |
65+ | request response |
5866 running := true.
5967 [ running ] whileTrue: [
60- temp1 := nil.
61- temp2 := nil.
62- temp1 := transport readMessage.
63- temp1
68+ request := transport readMessage.
69+ request
6470 ifNil: [ running := false ]
6571 ifNotNil: [
66- temp2 := self handleRequest: temp1 .
67- temp2 ifNotNil: [ transport writeMessage: (Json render: temp2 ) ]]].! !
72+ response := self handleRequest: request .
73+ response ifNotNil: [ transport writeMessage: (Json render: response ) ]]].! !
6874
6975!MCPServer methodsFor: 'running' stamp: ''!
7076stop
@@ -779,7 +785,7 @@ toolClassesInCategory: arg1
779785 argm3_5 name ]) asArray sorted.
780786 ^ Json render: temp4.! !
781787
782- !MCPServer methodsFor: 'tool implementations' stamp: ''!
788+ !MCPServer methodsFor: 'tool implementations' stamp: 'jmm 1/26/2026 09:30 '!
783789toolDefineClass: arg1
784790 | temp2 |
785791 temp2 := arg1
@@ -807,7 +813,7 @@ toolDefineMethod: arg1
807813 at: temp2 asSymbol
808814 ifAbsent: [ self error: 'Class not found: ' , temp2 ].
809815 temp5
810- compile : temp3
816+ compileSilently : temp3
811817 classified: temp4.
812818 ^ 'Method defined successfully'.! !
813819
@@ -924,25 +930,52 @@ toolSubclasses: arg1
924930 argm3_5 name ]) asArray sorted.
925931 ^ Json render: temp4.! !
926932
927- !MCPServer class methodsFor: 'system startup' stamp: ''!
928- startUp: arg1
929- | temp2 |
930- arg1 ifFalse: [ ^ self ].
931- SourceFiles
932- at: 2
933- put: nil.
934- temp2 := Smalltalk startUpArguments.
935- (temp2 includes: '--mcp') ifTrue: [ self startServer ].! !
933+ !MCPServer class methodsFor: 'system startup' stamp: 'jmm 1/26/2026 11:00'!
934+ startUp: resuming
935+ | args |
936+ resuming ifFalse: [ ^ self ].
937+ args := Smalltalk startUpArguments.
938+ (args includes: '--mcp') ifTrue: [ self startServer ].! !
939+
940+ !MCPServer class methodsFor: 'version' stamp: 'jmm 1/26/2026 11:00'!
941+ version
942+ "Return the MCP server version number.
943+ Increment when making changes that external tools should detect.
944+ Version history:
945+ 1 - Initial version
946+ 2 - Fixed toolDefineMethod: to use compileSilently: for headless operation
947+ 3 - (skipped)
948+ 4 - Fork MCP to background process, allowing GUI to display
949+ 5 - Set author initials to avoid prompt during class/method definition
950+ 6 - Port to OSProcess for non-blocking stdio (GUI responsive during MCP wait)
951+ 7 - Ensure ThisOSProcess is initialized before MCP transport accesses it
952+ 8 - Remove debug logging that blocked UI startup
953+ 9 - Simplify MCPTransport initialize
954+ 10 - Add extensive logging to debug UI startup hang
955+ 11 - Remove debug logging, use correct Cuis author API"
956+ ^ 11! !
936957
937- !MCPServer class methodsFor: 'instance creation' stamp: ''!
958+ !MCPServer class methodsFor: 'instance creation' stamp: 'jmm 1/26/2026 11:00 '!
938959startServer
939- (Delay forMilliseconds: 500) wait.
940- self new run.! !
960+ "Start MCP server in background process, allowing GUI to initialize normally"
961+ SourceFiles at: 2 put: nil. "Disable changes file for MCP mode"
962+ Utilities setAuthorName: 'ClaudeSmalltalk' initials: 'LLM'.
963+ [
964+ (Delay forMilliseconds: 500) wait.
965+ self new run
966+ ] forkAt: Processor userBackgroundPriority named: 'MCP Server'.! !
941967
942- !MCPTransport methodsFor: 'initialization' stamp: ''!
968+ !MCPTransport methodsFor: 'initialization' stamp: 'jmm 1/26/2026 11:00 '!
943969initialize
944- stdin := StdIOReadStream stdin.
945- stdout := StdIOWriteStream stdout.! !
970+ "Initialize using OSProcess for stdio with buffered async reads.
971+ BufferedAsyncFileReadStream uses semaphore-based waiting which
972+ allows the VM to schedule other processes (like the UI) while waiting for input."
973+
974+ | osProc |
975+ osProc := OSProcess thisOSProcess.
976+ stdin := osProc stdIn asBufferedAsyncFileReadStream.
977+ stdin setBlocking.
978+ stdout := osProc stdOut.! !
946979
947980!MCPTransport methodsFor: 'reading' stamp: ''!
948981readLine
@@ -994,13 +1027,8 @@ checkIfAlreadyRunningOrStoppedNoExit
9941027
9951028!SystemDictionary methodsFor: '*MCP-Server' stamp: ''!
9961029openSourcesAndChanges
997- | temp1 temp2 temp3 temp4 temp5 temp6 temp7 temp8 temp9 temp11 |
998- temp6 := false.
999- temp8 := 2.
1000- [
1001- temp8 <= 100 and: [ (temp7 := self getSystemAttribute: temp8) notNil ]] whileTrue: [
1002- (temp7 = '--mcp' or: [ temp7 = '-d' ]) ifTrue: [ temp6 := true ].
1003- temp8 := temp8 + 1 ].
1030+ "Always run without changes file - allows image to be distributed without .changes"
1031+ | temp1 temp9 temp3 |
10041032 temp1 := SourceFiles at: 1.
10051033 temp1 ifNil: [
10061034 temp9 := self defaultSourcesName asFullFileEntry.
@@ -1012,36 +1040,9 @@ openSourcesAndChanges
10121040 (temp1 isNil and: [ Preferences at: #warnIfNoSourcesFile ]) ifTrue: [
10131041 temp3 := 'Cuis cannot locate the sources file named ' , temp9 pathName , '.' , String newLineString , 'Please check that the file is properly named and is in the same directory as this image.'.
10141042 self logStartupError: temp3 ].
1015- temp6 ifTrue: [
1016- SourceFiles := Array
1017- with: temp1
1018- with: nil.
1019- ^ self ].
1020- temp2 := SourceFiles at: 2.
1021- temp2 ifNil: [
1022- temp9 := self defaultChangesName asFullFileEntry.
1023- temp5 := temp9 pathName.
1024- temp9 exists
1025- ifTrue: [
1026- temp4 := self lastQuitLogPosition.
1027- temp4 > 0 ifTrue: [
1028- temp9 readStreamDo: [ :argm11_12 |
1029- argm11_12 position: temp4.
1030- temp11 := argm11_12 nextChunk ].
1031- ((temp11 beginsWith: self tagHeader) and: [ temp11 includesSubString: 'priorSource: ' ]) ifFalse: [
1032- (Preferences at: #warnIfNoChangesFile) ifTrue: [ self logStartupError: 'Incorrect changes file: ' , temp5 , String newLineString , 'Missing code will be decompiled' , String newLineString , 'New source code will not be saved' ].
1033- temp9 := nil ]]]
1034- ifFalse: [
1035- (Preferences at: #warnIfNoChangesFile) ifTrue: [ self logStartupError: 'Could not find changes file: ' , temp5 , String newLineString , 'Missing code will be decompiled' , String newLineString , 'New source code will not be saved' ].
1036- temp9 := nil ].
1037- temp9 ifNotNil: [
1038- temp2 := [ temp9 appendStream ]
1039- on: FileWriteError
1040- do: [ self logStartupError: 'Could not write to changes file: ' , temp5 , String newLineString , 'Changes file will not be used.' , String newLineString , 'Missing code will be decompiled' , String newLineString , 'New source code will not be saved' ]]].
1041- ChangesInitialFileSize := temp2 ifNotNil: [ temp2 position ].
10421043 SourceFiles := Array
10431044 with: temp1
1044- with: temp2 .! !
1045+ with: nil .! !
10451046
10461047"Register MCPServer for startup"!
10471048Smalltalk addToStartUpList: MCPServer!
0 commit comments