Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ MCPSaveImageRecordingServer >> rpcToolCall: aToolName withParams: someArguments

(self toolRegistry isStaticToolNamed: aToolName) ifTrue: [ ^ super rpcToolCall: aToolName withParams: someArguments ].
^ super rpcToolCall: 'tool_call' withParams: {
(#toolName -> aToolName).
(#name -> aToolName).
(#arguments -> someArguments) } asDictionary
]

Expand Down
16 changes: 7 additions & 9 deletions src/MCP-Tests/MCPClassMutationRequestTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ MCPClassMutationRequestTest >> testCreateRequestParsesDefinitionArguments [
fromRequest: (MCPToolRequest new
tool: MCPToolCreateClass new;
arguments: {
(#className -> 'MCPGeneratedClass').
(#name -> 'MCPGeneratedClass').
(#superclassName -> 'Object').
(#packageName -> 'MCP-Generated').
(#tag -> 'Models').
(#comment -> 'Generated comment.').
(#force -> true).
(#slots -> #( 'firstName' 'lastName' )).
(#classSlots -> #( 'defaultName' )).
(#traits -> #( 'TComparable' )).
Expand All @@ -37,7 +36,6 @@ MCPClassMutationRequestTest >> testCreateRequestParsesDefinitionArguments [
self assert: request packageName equals: 'MCP-Generated'.
self assert: request tag equals: 'Models'.
self assert: request classComment equals: 'Generated comment.'.
self assert: request force.
self assert: request slotNames equals: #( 'firstName' 'lastName' ).
self assert: request slots equals: #( #firstName #lastName ).
self assert: request classSlotNames equals: #( 'defaultName' ).
Expand All @@ -61,7 +59,7 @@ MCPClassMutationRequestTest >> testUpdateCommandResolvesOmittedMovePackageName [
request := MCPToolRequest new
tool: tool;
arguments: {
(#className -> self class name asString).
(#name -> self class name asString).
(#tag -> 'Requests') } asDictionary;
yourself.
mutationRequest := tool parsedRequestFromToolRequest: request.
Expand All @@ -80,7 +78,7 @@ MCPClassMutationRequestTest >> testUpdateRequestParsesSlotArguments [
tool := MCPToolUpdateClassSlotName new.
request := MCPClassUpdateRequest
fromRequest: (self toolRequestWithArguments: {
(#className -> 'ExistingClass').
(#name -> 'ExistingClass').
(#slotAction -> 'rename').
(#slotName -> 'oldName').
(#newSlotName -> 'newName').
Expand Down Expand Up @@ -108,7 +106,7 @@ MCPClassMutationRequestTest >> testUpdateRequestTracksSuppliedProperties [
tool := MCPToolUpdateClassSlots new.
request := MCPClassUpdateRequest
fromRequest: (self toolRequestWithArguments: {
(#className -> 'ExistingClass').
(#name -> 'ExistingClass').
(#tag -> 'Models').
(#comment -> '').
(#force -> true).
Expand All @@ -125,7 +123,7 @@ MCPClassMutationRequestTest >> testUpdateRequestTracksSuppliedProperties [
self deny: (request hasSuppliedPropertyNamed: 'classSlots').
self assert: request slotNames isEmpty.
context := request requestedContext.
self assert: (context at: #className) equals: 'ExistingClass'.
self assert: (context at: #name) equals: 'ExistingClass'.
self assert: (context at: #tag) equals: 'Models'.
self assert: (context at: #comment) equals: ''.
self assert: (context at: #slots) equals: #( ).
Expand All @@ -138,14 +136,14 @@ MCPClassMutationRequestTest >> testUpdateRequestWithNoPatchArgumentsHasNoUpdates
| context request tool |
tool := MCPToolUpdateClassSlots new.
request := MCPClassUpdateRequest
fromRequest: (self toolRequestWithArguments: { (#className -> 'ExistingClass') } asDictionary)
fromRequest: (self toolRequestWithArguments: { (#name -> 'ExistingClass') } asDictionary)
tool: tool.
self assert: request className equals: 'ExistingClass'.
self deny: request hasUpdates.
self assert: request suppliedProperties isEmpty.
context := request requestedContext.
self assert: context size equals: 1.
self assert: (context at: #className) equals: 'ExistingClass'
self assert: (context at: #name) equals: 'ExistingClass'
]

{ #category : 'private' }
Expand Down
59 changes: 55 additions & 4 deletions src/MCP-Tests/MCPCommandErrorTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ MCPCommandErrorTest >> testClassAlreadyExistsErrorCarriesClassName [
details := error structuredDetails.
self assert: error errorCode equals: #ClassAlreadyExists.
self assert: (details at: #className) equals: 'MCPCommandErrorTestExisting'.
self assert: (details at: #message) equals: 'Class MCPCommandErrorTestExisting already exists.'
self assert: (details at: #message) equals: 'Class MCPCommandErrorTestExisting already exists.'.
self assert: ((details at: #guidance) includesSubstring: 'Use class_get').
self assert: ((details at: #guidance) includesSubstring: 'Do not use class_create')
]

{ #category : 'tests' }
Expand All @@ -52,17 +54,66 @@ MCPCommandErrorTest >> testMissingMethodErrorCarriesClassSideAndSelector [

| details error |
error := self capture: [
MCPCommandError signalMissingMethodInClassName: 'MCPCommandErrorTestTarget' classSide: true selector: 'value' ].
MCPCommandError
signalMissingMethodInClassName: 'MCPToolGetMethodTestTarget'
classSide: true
selector: 'mcpDefinitelyMissingSelectorForErrorTest' ].
details := error structuredDetails.
self assert: error errorCode equals: #MethodNotFound.
self assert: (details at: #errorClass) equals: 'MCPCommandError'.
self assert: (details at: #errorCode) equals: 'MethodNotFound'.
self assert: (details at: #className) equals: 'MCPCommandErrorTestTarget'.
self assert: (details at: #className) equals: 'MCPToolGetMethodTestTarget'.
self assert: (details at: #classSide).
self assert: (details at: #selector) equals: 'value'.
self assert: (details at: #selector) equals: 'mcpDefinitelyMissingSelectorForErrorTest'.
self assert: details keys asSet equals: #( errorClass errorCode message className classSide selector ) asSet
]

{ #category : 'tests' }
MCPCommandErrorTest >> testMissingMethodErrorForMissingClassHasNoSuggestions [

| details error |
error := self capture: [
MCPCommandError signalMissingMethodInClassName: 'MCPCommandErrorTestMissingClass' classSide: false selector: 'value' ].
details := error structuredDetails.
self assert: error errorCode equals: #ClassNotFound.
self assert: (details at: #className) equals: 'MCPCommandErrorTestMissingClass'.
self assert: (details at: #selector) equals: 'value'.
self deny: (details includesKey: #suggestions).
self assert: ((details at: #message) includesSubstring: 'Class MCPCommandErrorTestMissingClass does not exist')
]

{ #category : 'tests' }
MCPCommandErrorTest >> testMissingMethodErrorSuggestsFirstExactSuperclassMatch [

| details error suggestions |
error := self capture: [
MCPCommandError signalMissingMethodInClassName: 'MCPToolGetMethodTestTarget' classSide: false selector: 'yourself' ].
details := error structuredDetails.
suggestions := details at: #suggestions.
self assert: error errorCode equals: #MethodNotFound.
self assert: suggestions size equals: 1.
self assert: (suggestions first at: #className) equals: 'Object'.
self deny: (suggestions first at: #classSide).
self assert: (suggestions first at: #selector) equals: 'yourself'.
self assert: ((details at: #message) includesSubstring: 'Exact matches: Object>>yourself')
]

{ #category : 'tests' }
MCPCommandErrorTest >> testMissingMethodErrorSuggestsOnlyExactOppositeSideAndSuperclassMatches [

| details error suggestions |
error := self capture: [
MCPCommandError signalMissingMethodInClassName: 'MCPToolGetMethodTestTarget' classSide: false selector: 'readClassSide' ].
details := error structuredDetails.
suggestions := details at: #suggestions.
self assert: error errorCode equals: #MethodNotFound.
self assert: suggestions size equals: 1.
self assert: (suggestions first at: #className) equals: 'MCPToolGetMethodTestTarget'.
self assert: (suggestions first at: #classSide).
self assert: (suggestions first at: #selector) equals: 'readClassSide'.
self assert: ((details at: #message) includesSubstring: 'Exact matches: MCPToolGetMethodTestTarget class>>readClassSide')
]

{ #category : 'tests' }
MCPCommandErrorTest >> testMissingPackageErrorCarriesPackageName [

Expand Down
3 changes: 2 additions & 1 deletion src/MCP-Tests/MCPCreateClassCommandTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ MCPCreateClassCommandTest >> testExistingClassSignalsCommandError [
slots: nil
classSlots: nil) execute ].
self assert: error errorCode equals: #ClassAlreadyExists.
self assert: (error structuredDetails at: #className) equals: 'MCPCreateClassCommandTestGenerated'
self assert: (error structuredDetails at: #className) equals: 'MCPCreateClassCommandTestGenerated'.
self assert: ((error structuredDetails at: #guidance) includesSubstring: 'Use class_get')
]

{ #category : 'tests' }
Expand Down
88 changes: 44 additions & 44 deletions src/MCP-Tests/MCPJSONSchemaValidatorTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ MCPJSONSchemaValidatorTest >> baseRepresentativeArgumentsByToolClass [
^ {
(MCPToolEvaluate -> { (#code -> '1 + 1') } asDictionary).
(MCPToolRunTests -> { (#classes -> { 'MCPToolContractsTest' }) } asDictionary).
(MCPToolGetClass -> { (#className -> 'MCPTool') } asDictionary).
(MCPToolGetClass -> { (#name -> 'MCPTool') } asDictionary).
(MCPToolGetMethod -> {
(#className -> 'MCPTool').
(#selector -> 'name') } asDictionary).
Expand All @@ -25,42 +25,42 @@ MCPJSONSchemaValidatorTest >> baseRepresentativeArgumentsByToolClass [
(#selectors -> #( 'temporarySelector' )) } asDictionary).
(MCPToolSearchRepositories -> Dictionary new).
(MCPToolVerifyRepositoryIdentity -> {
(#repositoryName -> 'MCP').
(#name -> 'MCP').
(#branchName -> 'main') } asDictionary).
(MCPToolListRepositoryChanges -> { (#repositoryName -> 'MCP') } asDictionary).
(MCPToolDiscardRepositoryChanges -> { (#repositoryName -> 'MCP') } asDictionary).
(MCPToolListRepositoryChanges -> { (#name -> 'MCP') } asDictionary).
(MCPToolDiscardRepositoryChanges -> { (#name -> 'MCP') } asDictionary).
(MCPToolRepositoryOperation -> {
(#action -> 'update').
(#repositoryName -> 'MCP').
(#name -> 'MCP').
(#addPackageNames -> #( 'MCP' )) } asDictionary).
(MCPToolAddRepositoryRemote -> {
(#repositoryName -> 'MCP').
(#name -> 'MCP').
(#remoteName -> 'origin').
(#remoteUrl -> 'git@example.com:mcp.git') } asDictionary).
(MCPToolRemoveRepositoryRemote -> {
(#repositoryName -> 'MCP').
(#name -> 'MCP').
(#remoteName -> 'origin') } asDictionary).
(MCPToolUpdateRepositoryRemote -> {
(#repositoryName -> 'MCP').
(#name -> 'MCP').
(#remoteName -> 'origin').
(#remoteUrl -> 'git@example.com:mcp.git') } asDictionary).
(MCPToolExportRepository -> { (#repositoryName -> 'MCP') } asDictionary).
(MCPToolExportRepository -> { (#name -> 'MCP') } asDictionary).
(MCPToolCommitRepository -> {
(#repositoryName -> 'MCP').
(#name -> 'MCP').
(#message -> 'Example commit') } asDictionary).
(MCPToolFetchRepository -> { (#repositoryName -> 'MCP') } asDictionary).
(MCPToolPullRepository -> { (#repositoryName -> 'MCP') } asDictionary).
(MCPToolPushRepository -> { (#repositoryName -> 'MCP') } asDictionary).
(MCPToolFetchRepository -> { (#name -> 'MCP') } asDictionary).
(MCPToolPullRepository -> { (#name -> 'MCP') } asDictionary).
(MCPToolPushRepository -> { (#name -> 'MCP') } asDictionary).
(MCPToolCreateRepositoryBranch -> {
(#repositoryName -> 'MCP').
(#name -> 'MCP').
(#branchName -> 'example-branch') } asDictionary).
(MCPToolSwitchRepositoryBranch -> {
(#repositoryName -> 'MCP').
(#name -> 'MCP').
(#branchName -> 'main') } asDictionary).
(MCPToolCheckoutRepositoryBranch -> {
(#repositoryName -> 'MCP').
(#name -> 'MCP').
(#branchName -> 'main') } asDictionary).
(MCPToolAdoptRepositoryHead -> { (#repositoryName -> 'MCP') } asDictionary).
(MCPToolAdoptRepositoryHead -> { (#name -> 'MCP') } asDictionary).
(MCPToolLoadRepository -> {
(#owner -> 'ExampleOwner').
(#project -> 'ExampleProject').
Expand All @@ -70,8 +70,8 @@ MCPJSONSchemaValidatorTest >> baseRepresentativeArgumentsByToolClass [
(MCPToolSearchPackages -> Dictionary new).
(MCPToolClassMutation -> {
(#action -> 'update').
(#className -> 'MCPTool').
(#newClassName -> 'MCPToolRenamed') } asDictionary).
(#name -> 'MCPTool').
(#newName -> 'MCPToolRenamed') } asDictionary).
(MCPToolMethodMutation -> {
(#action -> 'create').
(#className -> 'MCPTool').
Expand Down Expand Up @@ -102,9 +102,9 @@ MCPJSONSchemaValidatorTest >> baseRepresentativeArgumentsByToolClass [
(MCPToolUpdateDebugMethod -> { (#sessionId -> 'debug-session-1') } asDictionary).
(MCPToolSearchMethodMetadata -> Dictionary new).
(MCPToolSearchTools -> Dictionary new).
(MCPToolGetTool -> { (#toolName -> 'class_search') } asDictionary).
(MCPToolGetTool -> { (#name -> 'class_search') } asDictionary).
(MCPToolCallTool -> {
(#toolName -> 'package_search').
(#name -> 'package_search').
(#arguments -> Dictionary new) } asDictionary) } asDictionary
]

Expand All @@ -113,54 +113,54 @@ MCPJSONSchemaValidatorTest >> classMutationRepresentativeArgumentsByToolClass [

^ {
(MCPToolCreateClass -> {
(#className -> 'MCPJSONSchemaValidatorTestTemporary').
(#name -> 'MCPJSONSchemaValidatorTestTemporary').
(#superclassName -> 'Object').
(#packageName -> 'MCP-Tests') } asDictionary).
(MCPToolUpdateClassName -> {
(#className -> 'MCPTool').
(#newClassName -> 'MCPToolRenamed') } asDictionary).
(#name -> 'MCPTool').
(#newName -> 'MCPToolRenamed') } asDictionary).
(MCPToolUpdateClassSuperclass -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#superclassName -> 'Object') } asDictionary).
(MCPToolUpdateClassPackage -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#tag -> 'Tools') } asDictionary).
(MCPToolUpdateClassComment -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#comment -> '') } asDictionary).
(MCPToolUpdateClassSlots -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#slots -> #( )) } asDictionary).
(MCPToolUpdateClassTraits -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#traits -> #( )) } asDictionary).
(MCPToolUpdateClassSideTraits -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#classTraits -> #( )) } asDictionary).
(MCPToolUpdateClassSharedVariables -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#sharedVariables -> #( )) } asDictionary).
(MCPToolUpdateClassSharedPools -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#sharedPools -> #( )) } asDictionary).
(MCPToolUpdateClassLayout -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#layout -> 'FixedLayout') } asDictionary).
(MCPToolAddClassSlot -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#slotName -> 'temporarySlot') } asDictionary).
(MCPToolRemoveClassSlot -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#slotName -> 'temporarySlot') } asDictionary).
(MCPToolUpdateClassSlotName -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#slotName -> 'temporarySlot').
(#newSlotName -> 'renamedTemporarySlot') } asDictionary).
(MCPToolPullUpClassSlot -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#slotName -> 'temporarySlot') } asDictionary).
(MCPToolPushDownClassSlot -> {
(#className -> 'MCPTool').
(#name -> 'MCPTool').
(#slotName -> 'temporarySlot') } asDictionary) } asDictionary
]

Expand Down Expand Up @@ -211,7 +211,7 @@ MCPJSONSchemaValidatorTest >> representativeArgumentsByToolClass [
at: MCPToolAttachRepository put: {
(#name -> 'MCP Test Repository').
(#location -> '/tmp/mcp-test-repository') } asDictionary;
at: MCPToolUpdateRepository put: { (#repositoryName -> 'MCP') } asDictionary;
at: MCPToolUpdateRepository put: { (#name -> 'MCP') } asDictionary;
at: MCPToolLoadBaseline put: { (#baseline -> 'ExampleBaseline') } asDictionary;
at: MCPToolRunTestCoverage put: {
(#classes -> { 'MCPToolContractsTest' }).
Expand Down Expand Up @@ -273,25 +273,25 @@ MCPJSONSchemaValidatorTest >> testCurrentToolInputSchemasAdvertiseGroupedSearchS
:each | each name ].
self
assert: (classProperties collect: [ :each | each name ]) asSet
equals: #( 'scope' 'className' 'tag' 'instanceSlot' 'classSlot' 'filterMode' 'caseSensitive' 'limit' 'offset' ) asSet.
equals: #( 'scope' 'name' 'tag' 'filterMode' 'limit' 'offset' ) asSet.
self
assert: (methodProperties collect: [ :each | each name ]) asSet
equals: #( 'scope' 'selector' 'protocol' 'filterMode' 'side' 'includeSource' 'limit' 'offset' 'caseSensitive' ) asSet.
self assert: classScopeProperties asSet equals: #( 'classes' 'packages' 'superclassesOf' 'subclassesOf' 'hierarchies' ) asSet.
self assert: classScopeProperties asSet equals: #( 'classes' 'packages' 'superclassesOf' 'subclassesOf' ) asSet.
self assert: methodScopeProperties asSet equals: classScopeProperties asSet
]

{ #category : 'tests' }
MCPJSONSchemaValidatorTest >> testCurrentToolRequestValidationRejectsOperationSpecificMissingArguments [

self
should: [ MCPToolCreateClass new requestFromToolCallArguments: { (#className -> 'MCPTool') } asDictionary ]
should: [ MCPToolCreateClass new requestFromToolCallArguments: { (#name -> 'MCPTool') } asDictionary ]
raise: MCPInvalidToolInput.
self
should: [ MCPToolUpdateClassPackage new requestFromToolCallArguments: { (#className -> 'MCPTool') } asDictionary ]
should: [ MCPToolUpdateClassPackage new requestFromToolCallArguments: { (#name -> 'MCPTool') } asDictionary ]
raise: MCPInvalidToolInput.
self
should: [ MCPToolCommitRepository new requestFromToolCallArguments: { (#repositoryName -> 'MCP') } asDictionary ]
should: [ MCPToolCommitRepository new requestFromToolCallArguments: { (#name -> 'MCP') } asDictionary ]
raise: MCPInvalidToolInput.
self
should: [ MCPToolRunTestCoverage new requestFromToolCallArguments: { (#classes -> { 'MCPToolContractsTest' }) } asDictionary ]
Expand Down
Loading
Loading