From ae4920d432ae49d3f1e1185fef590b1190c9e78d Mon Sep 17 00:00:00 2001 From: Gabriel-Darbord Date: Mon, 20 Jul 2026 10:02:52 +0200 Subject: [PATCH 1/5] Move tool metadata onto tool instances --- src/MCP-Tests/MCPHaltingTestTool.class.st | 6 + .../MCPSaveImageRecordingServer.class.st | 3 +- src/MCP-Tests/MCPToolAPITestCase.class.st | 15 +- .../MCPToolChangeHistoryToolsTest.class.st | 22 +- src/MCP-Tests/MCPToolContractsTest.class.st | 523 +++++--------- .../MCPToolMethodLookupTest.class.st | 43 +- .../MCPToolRepositoryOperationTest.class.st | 4 +- .../MCPToolRepositoryToolsTest.class.st | 21 +- src/MCP/MCP.class.st | 30 +- src/MCP/MCPGetToolCommand.class.st | 8 +- src/MCP/MCPSearchToolsCommand.class.st | 15 +- src/MCP/MCPTool.class.st | 117 ++- src/MCP/MCPToolCallTool.class.st | 24 + src/MCP/MCPToolCaptureScreenshot.class.st | 24 + src/MCP/MCPToolChangeHistory.class.st | 12 + .../MCPToolCheckoutRepositoryBranch.class.st | 6 + src/MCP/MCPToolClassMutation.class.st | 12 + src/MCP/MCPToolCompileMethod.class.st | 6 + src/MCP/MCPToolCreateClass.class.st | 6 + src/MCP/MCPToolDebug.class.st | 12 + src/MCP/MCPToolDebugCapture.class.st | 12 + src/MCP/MCPToolDebugEvaluate.class.st | 12 + src/MCP/MCPToolDebugState.class.st | 12 + src/MCP/MCPToolDebugTest.class.st | 12 + src/MCP/MCPToolDebugVariables.class.st | 12 + src/MCP/MCPToolEvaluate.class.st | 30 + src/MCP/MCPToolExposurePolicy.class.st | 55 +- src/MCP/MCPToolGetClass.class.st | 24 + src/MCP/MCPToolGetDebugSession.class.st | 12 + src/MCP/MCPToolGetMethod.class.st | 24 + src/MCP/MCPToolGetTool.class.st | 24 + .../MCPToolListChangeHistoryEntries.class.st | 12 + .../MCPToolListChangeHistoryFiles.class.st | 12 + src/MCP/MCPToolListDebugBreakpoints.class.st | 12 + src/MCP/MCPToolListDebugSessions.class.st | 12 + src/MCP/MCPToolListRepositoryChanges.class.st | 18 + src/MCP/MCPToolLoadBaseline.class.st | 6 + src/MCP/MCPToolLoadMetacello.class.st | 12 + src/MCP/MCPToolLoadRepository.class.st | 6 + src/MCP/MCPToolMethodLookupOperation.class.st | 6 + src/MCP/MCPToolMethodMutation.class.st | 12 + src/MCP/MCPToolMethodSearch.class.st | 12 + src/MCP/MCPToolRegistration.class.st | 33 +- src/MCP/MCPToolRegistry.class.st | 664 ++---------------- src/MCP/MCPToolRemoveClasses.class.st | 6 + src/MCP/MCPToolRemoveMethods.class.st | 6 + src/MCP/MCPToolRepositoryOperation.class.st | 12 + src/MCP/MCPToolRewriteMethods.class.st | 6 + src/MCP/MCPToolRunTestCoverage.class.st | 6 + src/MCP/MCPToolRunTests.class.st | 30 + src/MCP/MCPToolSearch.class.st | 12 + src/MCP/MCPToolSearchClasses.class.st | 12 + src/MCP/MCPToolSearchDebugCandidates.class.st | 12 + .../MCPToolSearchMethodImplementors.class.st | 6 + src/MCP/MCPToolSearchMethodMetadata.class.st | 6 + src/MCP/MCPToolSearchMethodSenders.class.st | 6 + src/MCP/MCPToolSearchMethodSource.class.st | 6 + src/MCP/MCPToolSearchPackages.class.st | 12 + src/MCP/MCPToolSearchRepositories.class.st | 12 + src/MCP/MCPToolSearchTools.class.st | 12 + src/MCP/MCPToolUpdateClassLayout.class.st | 6 + .../MCPToolUpdateClassSharedPools.class.st | 6 + ...MCPToolUpdateClassSharedVariables.class.st | 6 + src/MCP/MCPToolUpdateClassSideTraits.class.st | 6 + src/MCP/MCPToolUpdateClassTraits.class.st | 6 + src/MCP/MCPToolUpdateMethodSelector.class.st | 6 + .../MCPToolVerifyRepositoryIdentity.class.st | 18 + 67 files changed, 1071 insertions(+), 1100 deletions(-) diff --git a/src/MCP-Tests/MCPHaltingTestTool.class.st b/src/MCP-Tests/MCPHaltingTestTool.class.st index 1b25d48..d2b9731 100644 --- a/src/MCP-Tests/MCPHaltingTestTool.class.st +++ b/src/MCP-Tests/MCPHaltingTestTool.class.st @@ -50,6 +50,12 @@ MCPHaltingTestTool >> executeWithRequest: aRequest [ (Smalltalk globals at: #Halt) signal: 'mcp tool halt test' ] +{ #category : 'metadata' } +MCPHaltingTestTool >> groupName [ + + ^ 'plugins' +] + { #category : 'metadata' } MCPHaltingTestTool >> title [ diff --git a/src/MCP-Tests/MCPSaveImageRecordingServer.class.st b/src/MCP-Tests/MCPSaveImageRecordingServer.class.st index 2c160bf..bc440e7 100644 --- a/src/MCP-Tests/MCPSaveImageRecordingServer.class.st +++ b/src/MCP-Tests/MCPSaveImageRecordingServer.class.st @@ -28,8 +28,7 @@ MCPSaveImageRecordingServer >> didSaveImageSession [ { #category : 'private - calling' } MCPSaveImageRecordingServer >> rpcToolCall: aToolName withParams: someArguments [ - (MCPToolRegistry isStaticTool: aToolName) ifTrue: [ - ^ super rpcToolCall: aToolName withParams: someArguments ]. + (self toolExposurePolicy isStaticTool: aToolName) ifTrue: [ ^ super rpcToolCall: aToolName withParams: someArguments ]. ^ super rpcToolCall: 'tool_call' withParams: { (#toolName -> aToolName). (#arguments -> someArguments) } asDictionary diff --git a/src/MCP-Tests/MCPToolAPITestCase.class.st b/src/MCP-Tests/MCPToolAPITestCase.class.st index 30fe57f..9bcab12 100644 --- a/src/MCP-Tests/MCPToolAPITestCase.class.st +++ b/src/MCP-Tests/MCPToolAPITestCase.class.st @@ -51,11 +51,10 @@ MCPToolAPITestCase >> callRawToolNamed: aToolName withArguments: someArguments [ { #category : 'private - calling' } MCPToolAPITestCase >> callToolNamed: aToolName withArguments: someArguments [ - (MCPToolRegistry isStaticTool: aToolName) ifTrue: [ - ^ self callRawToolNamed: aToolName withArguments: someArguments ]. - ^ self - callDiscoveredToolNamed: aToolName - withArguments: someArguments + | mcp | + mcp := self mcpWithoutObservabilityExport. + (mcp toolExposurePolicy isStaticTool: aToolName) ifTrue: [ ^ self callRawToolNamed: aToolName withArguments: someArguments ]. + ^ self callDiscoveredToolNamed: aToolName withArguments: someArguments ] { #category : 'private - results' } @@ -131,6 +130,12 @@ MCPToolAPITestCase >> summaryFrom: aResult [ ^ (self structuredContentFrom: aResult) at: #summary ] +{ #category : 'support' } +MCPToolAPITestCase >> toolRegistry [ + + ^ self mcpWithoutObservabilityExport toolRegistry +] + { #category : 'private - parsing' } MCPToolAPITestCase >> unvalidatedToolRequestForTool: aTool arguments: someArguments [ diff --git a/src/MCP-Tests/MCPToolChangeHistoryToolsTest.class.st b/src/MCP-Tests/MCPToolChangeHistoryToolsTest.class.st index cd1f080..d0eb3a8 100644 --- a/src/MCP-Tests/MCPToolChangeHistoryToolsTest.class.st +++ b/src/MCP-Tests/MCPToolChangeHistoryToolsTest.class.st @@ -19,26 +19,16 @@ MCPToolChangeHistoryToolsTest >> inputPropertyNamesFor: aTool [ MCPToolChangeHistoryToolsTest >> testChangeHistoryToolsAreDiscoverableWithExpectedRisk [ | metadataByName result toolNames | - result := self - callToolNamed: 'tool_search' - withArguments: { (#group -> 'history') } asDictionary. - metadataByName := (((self dataFrom: result) at: #tools) collect: [ :each | - (each at: #name) -> each ]) asDictionary. + result := self callToolNamed: 'tool_search' withArguments: { (#group -> 'history') } asDictionary. + metadataByName := (((self dataFrom: result) at: #tools) collect: [ :each | (each at: #name) -> each ]) asDictionary. toolNames := metadataByName keys. - #( 'history_file_list' 'history_entry_list' 'history_entry_apply' - 'history_entry_revert' ) do: [ :toolName | + #( 'history_file_list' 'history_entry_list' 'history_entry_apply' 'history_entry_revert' ) do: [ :toolName | | metadata tool | self assert: (toolNames includes: toolName). - tool := MCPToolRegistry - toolNamed: toolName - ifAbsent: [ self fail: 'Missing tool ' , toolName ]. - self - assert: (MCPToolRegistry exposureForTool: tool) - equals: 'discoverable'. + tool := self toolRegistry toolNamed: toolName ifAbsent: [ self fail: 'Missing tool ' , toolName ]. + self assert: tool defaultExposure equals: 'discoverable'. metadata := metadataByName at: toolName. - self - assert: metadata keys asSet - equals: #( name description ) asSet ] + self assert: metadata keys asSet equals: #( name description ) asSet ] ] { #category : 'tests' } diff --git a/src/MCP-Tests/MCPToolContractsTest.class.st b/src/MCP-Tests/MCPToolContractsTest.class.st index b1a62a6..5d85e52 100644 --- a/src/MCP-Tests/MCPToolContractsTest.class.st +++ b/src/MCP-Tests/MCPToolContractsTest.class.st @@ -878,102 +878,28 @@ MCPToolContractsTest >> testAllToolsUseParsedRequestsAndCommands [ { #category : 'tests' } MCPToolContractsTest >> testBrowseIconToolGroupsKeepRelatedToolsTogether [ - | orderedTools | - orderedTools := OrderedCollection new. - MCPTool toolGroups do: [ :group | orderedTools addAll: group value ]. - self - assert: orderedTools asSet - equals: MCPTool concreteSubclasses asSet. - self assert: orderedTools asArray equals: { - MCPToolSearchTools. - MCPToolGetTool. - MCPToolCallTool. - MCPToolSearchRepositories. - MCPToolVerifyRepositoryIdentity. - MCPToolListRepositoryChanges. - MCPToolDiscardRepositoryChanges. - MCPToolCreateRepository. - MCPToolAttachRepository. - MCPToolUpdateRepository. - MCPToolAddRepositoryRemote. - MCPToolRemoveRepositoryRemote. - MCPToolUpdateRepositoryRemote. - - MCPToolExportRepository. - MCPToolCommitRepository. - MCPToolFetchRepository. - MCPToolPullRepository. - MCPToolPushRepository. - MCPToolCreateRepositoryBranch. - MCPToolSwitchRepositoryBranch. - MCPToolCheckoutRepositoryBranch. - MCPToolAdoptRepositoryHead. - MCPToolLoadBaseline. - MCPToolLoadRepository. - MCPToolSearchPackages. - MCPToolSearchClasses. - MCPToolGetClass. - MCPToolCreateClass. - MCPToolUpdateClassName. - MCPToolUpdateClassSuperclass. - MCPToolUpdateClassPackage. - MCPToolUpdateClassComment. - MCPToolUpdateClassSlots. - MCPToolUpdateClassTraits. - MCPToolUpdateClassSideTraits. - MCPToolUpdateClassSharedVariables. - MCPToolUpdateClassSharedPools. - MCPToolUpdateClassLayout. - MCPToolAddClassSlot. - MCPToolRemoveClassSlot. - MCPToolUpdateClassSlotName. - MCPToolPullUpClassSlot. - MCPToolPushDownClassSlot. - MCPToolRemoveClasses. - MCPToolSearchMethodMetadata. - MCPToolSearchMethodSource. - MCPToolSearchEquivalentMethods. - MCPToolSearchMethodImplementors. - MCPToolSearchMethodSenders. - MCPToolSearchClassReferences. - MCPToolSearchVariableReferences. - MCPToolGetMethod. - MCPToolCompileMethod. - MCPToolUpdateMethodSelector. - MCPToolUpdateMethodProtocol. - MCPToolRewriteMethods. - MCPToolRemoveMethods. - MCPToolListChangeHistoryFiles. - MCPToolListChangeHistoryEntries. - MCPToolApplyChangeHistoryEntries. - MCPToolRevertChangeHistoryEntries. - MCPToolRunTests. - MCPToolRunTestCoverage. - MCPToolCaptureScreenshot. - MCPToolListDebugSessions. - MCPToolGetDebugSession. - MCPToolRemoveDebugSession. - MCPToolSearchDebugCandidates. - MCPToolAttachDebugCandidate. - MCPToolDebugState. - MCPToolDebugVariables. - MCPToolDebugEvaluate. - MCPToolStepIntoDebugSession. - MCPToolStepOverDebugSession. - MCPToolStepThroughDebugSession. - MCPToolRestartDebugSession. - MCPToolResumeDebugSession. - MCPToolTerminateDebugSession. - MCPToolListDebugBreakpoints. - MCPToolSetDebugBreakpoint. - MCPToolRemoveDebugBreakpoint. - MCPToolEnableDebugBreakpoint. - MCPToolDisableDebugBreakpoint. - MCPToolClearDebugBreakpoints. - MCPToolDebugCapture. - MCPToolDebugTest. - MCPToolUpdateDebugMethod. - MCPToolEvaluate } + | builtInToolClasses groupedToolClasses | + builtInToolClasses := MCPTool concreteSubclasses select: [ :toolClass | toolClass isBuiltIn ]. + groupedToolClasses := OrderedCollection new. + MCPTool toolGroups do: [ :group | + group value do: [ :toolClass | self assert: toolClass new groupName equals: group key ]. + groupedToolClasses addAll: group value ]. + self assert: groupedToolClasses asSet equals: builtInToolClasses asSet +] + +{ #category : 'tests' } +MCPToolContractsTest >> testBuiltInToolsProvideCatalogMetadata [ + + | validExposures validMutabilities validRisks | + validExposures := #( 'static' 'discoverable' ). + validMutabilities := #( 'read' 'execute' 'write' ). + validRisks := #( 'low' 'medium' 'high' ). + self toolRegistry publicTools do: [ :tool | + self deny: tool groupName equals: 'other'. + self assert: (validExposures includes: tool defaultExposure). + self assert: (validMutabilities includes: tool mutability). + self assert: (validRisks includes: tool risk). + self assert: tool keywords notEmpty ] ] { #category : 'tests' } @@ -1320,27 +1246,17 @@ MCPToolContractsTest >> testDebugInputSchemasAdvertiseExpansionBounds [ MCPToolContractsTest >> testDebuggerToolsAreDiscoverableOnly [ | debugNames metadataByName result staticToolNames | - debugNames := #( 'debug_session_list' 'debug_session_get' - 'debug_session_remove' 'debug_candidate_search' - 'debug_candidate_attach' 'debug_state_get' - 'debug_variable_get' 'debug_expression_evaluate' - 'debug_session_step_into' 'debug_session_step_over' - 'debug_session_step_through' - 'debug_session_restart' 'debug_session_resume' - 'debug_session_terminate' 'debug_breakpoint_list' - 'debug_breakpoint_set' 'debug_breakpoint_remove' - 'debug_breakpoint_enable' 'debug_breakpoint_disable' - 'debug_breakpoint_clear' 'debug_capture' - 'debug_test_run' 'debug_method_update' ). - staticToolNames := MCPToolRegistry staticToolNames. + debugNames := #( 'debug_session_list' 'debug_session_get' 'debug_session_remove' 'debug_candidate_search' + 'debug_candidate_attach' 'debug_state_get' 'debug_variable_get' 'debug_expression_evaluate' + 'debug_session_step_into' 'debug_session_step_over' 'debug_session_step_through' 'debug_session_restart' + 'debug_session_resume' 'debug_session_terminate' 'debug_breakpoint_list' 'debug_breakpoint_set' + 'debug_breakpoint_remove' 'debug_breakpoint_enable' 'debug_breakpoint_disable' 'debug_breakpoint_clear' + 'debug_capture' 'debug_test_run' 'debug_method_update' ). + staticToolNames := self toolRegistry staticToolNames. debugNames do: [ :each | self deny: (staticToolNames includes: each) ]. - result := self - callToolNamed: 'tool_search' - withArguments: { (#group -> 'debugging') } asDictionary. - metadataByName := (((self dataFrom: result) at: #tools) collect: [ - :each | (each at: #name) -> each ]) asDictionary. - debugNames do: [ :each | - self assert: (metadataByName includesKey: each) ] + result := self callToolNamed: 'tool_search' withArguments: { (#group -> 'debugging') } asDictionary. + metadataByName := (((self dataFrom: result) at: #tools) collect: [ :each | (each at: #name) -> each ]) asDictionary. + debugNames do: [ :each | self assert: (metadataByName includesKey: each) ] ] { #category : 'tests' } @@ -1401,27 +1317,19 @@ MCPToolContractsTest >> testDiscoveryUsesConfiguredStaticToolNames [ | createContract metadataByName result searchContract server | server := self mcpWithoutObservabilityExport. - server staticToolNames: - #( 'tool_search' 'tool_get' 'tool_call' 'repository_search' ). - result := server - rpcToolCall: 'tool_search' - withParams: { (#query -> 'repository') } asDictionary. - metadataByName := (((self dataFrom: result) at: #tools) collect: [ :each | - (each at: #name) -> each ]) asDictionary. + server staticToolNames: #( 'tool_search' 'tool_get' 'tool_call' 'repository_search' ). + result := server rpcToolCall: 'tool_search' withParams: { (#query -> 'repository') } asDictionary. + metadataByName := (((self dataFrom: result) at: #tools) collect: [ :each | (each at: #name) -> each ]) asDictionary. self assert: (metadataByName includesKey: 'repository_search'). self assert: (metadataByName includesKey: 'repository_create'). - searchContract := (self dataFrom: (server - rpcToolCall: 'tool_get' - withParams: { (#toolName -> 'repository_search') } asDictionary)) at: #tool. - createContract := (self dataFrom: (server - rpcToolCall: 'tool_get' - withParams: { (#toolName -> 'repository_create') } asDictionary)) at: #tool. - self - assert: searchContract keys asSet - equals: #( name description inputSchema ) asSet. - self - assert: createContract keys asSet - equals: #( name description inputSchema ) asSet + searchContract := (self dataFrom: + (server rpcToolCall: 'tool_get' withParams: { (#toolName -> 'repository_search') } asDictionary)) at: #tool. + createContract := (self dataFrom: + (server rpcToolCall: 'tool_get' withParams: { (#toolName -> 'repository_create') } asDictionary)) at: #tool. + self assert: searchContract keys asSet equals: #( name description group exposure mutability risk keywords inputSchema ) asSet. + self assert: createContract keys asSet equals: #( name description group exposure mutability risk keywords inputSchema ) asSet. + self assert: (searchContract at: #exposure) equals: 'static'. + self assert: (createContract at: #exposure) equals: 'discoverable' ] { #category : 'tests - evaluate' } @@ -1501,6 +1409,18 @@ MCPToolContractsTest >> testEvaluateReturnsRuntimeFailureInformation [ self assert: (text includesSubstring: 'Signaler context:') ] +{ #category : 'tests' } +MCPToolContractsTest >> testExposurePolicyOverridesToolDefault [ + + | policy registry tool | + registry := MCPToolRegistry new. + tool := registry toolNamed: 'method_protocol_update' ifAbsent: [ self fail ]. + policy := MCPToolExposurePolicy forRegistry: registry. + policy staticToolNames: (registry staticToolNames copyWith: tool name). + self assert: tool defaultExposure equals: 'discoverable'. + self assert: (policy isStaticTool: tool name) +] + { #category : 'tests' } MCPToolContractsTest >> testGetClassHasAccurateNameAndRequiredArguments [ @@ -1551,7 +1471,12 @@ MCPToolContractsTest >> testGetToolReturnsSchemaForCatalogTool [ result := self callToolNamed: 'tool_get' withArguments: { (#toolName -> 'repository_create') } asDictionary. contract := (self dataFrom: result) at: #tool. self assert: (contract at: #name) equals: 'repository_create'. - self assert: contract keys asSet equals: #( name description inputSchema ) asSet. + self assert: contract keys asSet equals: #( name description group exposure mutability risk keywords inputSchema ) asSet. + self assert: (contract at: #group) equals: 'repositories'. + self assert: (contract at: #exposure) equals: 'discoverable'. + self assert: (contract at: #mutability) equals: 'write'. + self assert: (contract at: #risk) equals: 'high'. + self assert: ((contract at: #keywords) includes: 'repo'). inputProperties := ((contract at: #inputSchema) at: #properties) keys. self assert: inputProperties asSet equals: #( 'name' 'location' 'packageNames' 'subdirectory' ) asSet. self assert: ((contract at: #inputSchema) at: #required) asArray equals: #( 'name' 'location' ). @@ -2240,33 +2165,19 @@ MCPToolContractsTest >> testQueryScopeDescriptionsExplainScopeSemantics [ { #category : 'tests' } MCPToolContractsTest >> testRPCAdvertisesStaticToolSurfaceOnly [ - | discoverableToolNames expectedStaticToolNames toolNames | - toolNames := (self mcpWithoutObservabilityExport rpcToolsList at: - #tools) collect: [ :each | each at: #name ]. - expectedStaticToolNames := #( 'tool_search' 'tool_get' 'tool_call' - 'package_search' 'class_search' - 'class_get' 'class_create' - 'method_metadata_search' - 'method_source_search' - 'method_implementor_search' - 'method_sender_search' - 'method_get' 'method_compile' - 'method_selector_update' + | discoverableToolNames expectedStaticToolNames registry toolNames | + registry := self toolRegistry. + toolNames := (self mcpWithoutObservabilityExport rpcToolsList at: #tools) collect: [ :each | each at: #name ]. + expectedStaticToolNames := #( 'tool_search' 'tool_get' 'tool_call' 'package_search' 'class_search' 'class_get' 'class_create' + 'method_metadata_search' 'method_source_search' 'method_implementor_search' + 'method_sender_search' 'method_get' 'method_compile' 'method_selector_update' 'image_evaluate' 'test_run' ). - discoverableToolNames := MCPToolRegistry publicToolNames difference: - MCPToolRegistry staticToolNames. - self - assert: toolNames asSet - equals: MCPToolRegistry staticToolNames asSet. - self - assert: MCPToolRegistry staticToolNames asSet - equals: expectedStaticToolNames asSet. - expectedStaticToolNames do: [ :each | - self assert: (toolNames includes: each) ]. - discoverableToolNames do: [ :each | - self deny: (toolNames includes: each) ]. - self assert: - (MCPToolRegistry publicToolNames includes: 'method_protocol_update'). + discoverableToolNames := registry publicToolNames difference: registry staticToolNames. + self assert: toolNames asSet equals: registry staticToolNames asSet. + self assert: registry staticToolNames asSet equals: expectedStaticToolNames asSet. + expectedStaticToolNames do: [ :each | self assert: (toolNames includes: each) ]. + discoverableToolNames do: [ :each | self deny: (toolNames includes: each) ]. + self assert: (registry publicToolNames includes: 'method_protocol_update'). self deny: (toolNames includes: 'method_protocol_update') ] @@ -2993,18 +2904,11 @@ MCPToolContractsTest >> testSearchToolSchemasAdvertiseGroupedScopeProperties [ MCPToolContractsTest >> testSearchToolsIncludesStaticSurface [ | metadataByName result staticTool | - result := self - callToolNamed: 'tool_search' - withArguments: { (#query -> 'classes') } asDictionary. - metadataByName := (((self dataFrom: result) at: #tools) collect: [ - :each | (each at: #name) -> each ]) asDictionary. + result := self callToolNamed: 'tool_search' withArguments: { (#query -> 'classes') } asDictionary. + metadataByName := (((self dataFrom: result) at: #tools) collect: [ :each | (each at: #name) -> each ]) asDictionary. self assert: (metadataByName includesKey: 'class_search'). - staticTool := MCPToolRegistry - toolNamed: 'class_search' - ifAbsent: [ self fail: 'Missing class_search tool' ]. - self - assert: (MCPToolRegistry exposureForTool: staticTool) - equals: 'static' + staticTool := self toolRegistry toolNamed: 'class_search' ifAbsent: [ self fail: 'Missing class_search tool' ]. + self assert: staticTool defaultExposure equals: 'static' ] { #category : 'tests' } @@ -3013,14 +2917,8 @@ MCPToolContractsTest >> testSearchToolsKeywordsAreSingleSearchTerms [ | evaluateTool searchTool | searchTool := MCPToolSearchTools new. evaluateTool := MCPToolEvaluate new. - self - assert: (MCPToolRegistry keywordsForTool: searchTool) asSet - equals: #( 'tool' 'catalog' 'search' ) asSet. - self - assert: (MCPToolRegistry keywordsForTool: evaluateTool) asSet - equals: - #( 'image' 'evaluate' 'runtime' 'expression' 'smalltalk' - 'code' ) asSet + self assert: searchTool keywords asSet equals: #( 'tool' 'catalog' 'search' ) asSet. + self assert: evaluateTool keywords asSet equals: #( 'image' 'evaluate' 'runtime' 'expression' 'smalltalk' 'code' ) asSet ] { #category : 'tests' } @@ -3228,44 +3126,35 @@ MCPToolContractsTest >> testServerInitializationLeavesIconCacheEmpty [ self assert: MCPStructureIcon cachedRenderedIconCount equals: 0 ] +{ #category : 'tests' } +MCPToolContractsTest >> testSingularSearchTermsUseGenericInflectionRules [ + + | registry | + registry := MCPToolRegistry new. + self assert: (registry singularSearchTermFor: 'classes') equals: 'class'. + self assert: (registry singularSearchTermFor: 'repositories') equals: 'repository'. + self assert: (registry singularSearchTermFor: 'histories') equals: 'history' +] + { #category : 'tests' } MCPToolContractsTest >> testSpecializedClassAndMethodToolsAreDiscoverableOnly [ | discoverableOnlyNames metadataByName | - discoverableOnlyNames := #( 'class_name_update' 'class_superclass_update' - 'class_package_update' - 'class_comment_update' - 'class_slots_update' 'class_traits_update' - 'class_side_traits_update' - 'class_shared_variables_update' - 'class_shared_pools_update' - 'class_layout_update' - 'class_slot_add' 'class_slot_remove' - 'class_slot_name_update' - 'class_slot_pull_up' 'class_slot_push_down' - 'method_equivalent_search' - 'method_class_reference_search' - 'method_variable_reference_search' - 'test_coverage_run' ). + discoverableOnlyNames := #( 'class_name_update' 'class_superclass_update' 'class_package_update' 'class_comment_update' + 'class_slots_update' 'class_traits_update' 'class_side_traits_update' 'class_shared_variables_update' + 'class_shared_pools_update' 'class_layout_update' 'class_slot_add' 'class_slot_remove' + 'class_slot_name_update' 'class_slot_pull_up' 'class_slot_push_down' 'method_equivalent_search' + 'method_class_reference_search' 'method_variable_reference_search' 'test_coverage_run' ). metadataByName := Dictionary new. - (self allToolSearchMetadataForArguments: Dictionary new) do: [ :each | - metadataByName at: (each at: #name) put: each ]. + (self allToolSearchMetadataForArguments: Dictionary new) do: [ :each | metadataByName at: (each at: #name) put: each ]. discoverableOnlyNames do: [ :each | - | metadata tool | - self assert: (MCPToolRegistry publicToolNames includes: each). - self deny: (MCPToolRegistry staticToolNames includes: each). - tool := MCPToolRegistry - toolNamed: each - ifAbsent: [ self fail: 'Missing tool ' , each ]. - self - assert: (MCPToolRegistry exposureForTool: tool) - equals: 'discoverable'. - metadata := metadataByName - at: each - ifAbsent: [ self fail: 'Missing discoverable metadata for ' , each ]. - self - assert: metadata keys asSet - equals: #( name description ) asSet ] + | metadata tool | + self assert: (self toolRegistry publicToolNames includes: each). + self deny: (self toolRegistry staticToolNames includes: each). + tool := self toolRegistry toolNamed: each ifAbsent: [ self fail: 'Missing tool ' , each ]. + self assert: tool defaultExposure equals: 'discoverable'. + metadata := metadataByName at: each ifAbsent: [ self fail: 'Missing discoverable metadata for ' , each ]. + self assert: metadata keys asSet equals: #( name description ) asSet ] ] { #category : 'tests' } @@ -3365,160 +3254,100 @@ MCPToolContractsTest >> testToolNamesFollowPublicNamingConvention [ { #category : 'tests' } MCPToolContractsTest >> testToolRegistryAcceptsPluginToolRegistration [ - | registration tool | - MCPToolRegistry unregisterToolNamed: MCPHaltingTestTool toolName. - [ - registration := MCPToolRegistry - registerToolClass: MCPHaltingTestTool - groupName: 'plugins' - defaultExposure: 'discoverable'. - tool := MCPToolRegistry - toolNamed: MCPHaltingTestTool toolName - ifAbsent: [ self fail ]. - self assert: tool == registration tool. - self deny: (MCPToolExposurePolicy default isStaticTool: tool name). - self assert: ((MCPToolRegistry publicToolGroups - detect: [ :each | each key = 'plugins' ] - ifNone: [ self fail ]) value includes: MCPHaltingTestTool) ] - ensure: [ - MCPToolRegistry unregisterToolNamed: MCPHaltingTestTool toolName ] + | registration registry tool | + registry := MCPToolRegistry new. + registration := registry registerToolClass: MCPHaltingTestTool. + tool := registry toolNamed: MCPHaltingTestTool toolName ifAbsent: [ self fail ]. + self assert: tool == registration tool. + self deny: ((MCPToolExposurePolicy forRegistry: registry) isStaticTool: tool name). + self assert: + ((registry publicToolGroups detect: [ :each | each key = 'plugins' ] ifNone: [ self fail ]) value includes: MCPHaltingTestTool) ] { #category : 'tests' } MCPToolContractsTest >> testToolRegistryNamesUseClassSideMetadata [ - | names | - names := MCPToolRegistry publicToolClasses collect: [ :each | - each toolName ]. - self assert: MCPToolRegistry publicToolNames equals: names. + | names registry | + registry := self toolRegistry. + names := registry publicToolClasses collect: [ :each | each toolName ]. + self assert: registry publicToolNames equals: names. self assert: MCPToolSearchClasses toolName equals: 'class_search'. self assert: MCPToolCreateClass toolName equals: 'class_create'. - self - assert: MCPToolUpdateClassName toolName - equals: 'class_name_update'. - self - assert: MCPToolUpdateClassSuperclass toolName - equals: 'class_superclass_update'. - self - assert: MCPToolUpdateClassPackage toolName - equals: 'class_package_update'. - self - assert: MCPToolUpdateClassComment toolName - equals: 'class_comment_update'. - self - assert: MCPToolUpdateClassSlots toolName - equals: 'class_slots_update'. - self - assert: MCPToolUpdateClassTraits toolName - equals: 'class_traits_update'. - self - assert: MCPToolUpdateClassSideTraits toolName - equals: 'class_side_traits_update'. - self - assert: MCPToolUpdateClassSharedVariables toolName - equals: 'class_shared_variables_update'. - self - assert: MCPToolUpdateClassSharedPools toolName - equals: 'class_shared_pools_update'. - self - assert: MCPToolUpdateClassLayout toolName - equals: 'class_layout_update'. + self assert: MCPToolUpdateClassName toolName equals: 'class_name_update'. + self assert: MCPToolUpdateClassSuperclass toolName equals: 'class_superclass_update'. + self assert: MCPToolUpdateClassPackage toolName equals: 'class_package_update'. + self assert: MCPToolUpdateClassComment toolName equals: 'class_comment_update'. + self assert: MCPToolUpdateClassSlots toolName equals: 'class_slots_update'. + self assert: MCPToolUpdateClassTraits toolName equals: 'class_traits_update'. + self assert: MCPToolUpdateClassSideTraits toolName equals: 'class_side_traits_update'. + self assert: MCPToolUpdateClassSharedVariables toolName equals: 'class_shared_variables_update'. + self assert: MCPToolUpdateClassSharedPools toolName equals: 'class_shared_pools_update'. + self assert: MCPToolUpdateClassLayout toolName equals: 'class_layout_update'. self assert: MCPToolAddClassSlot toolName equals: 'class_slot_add'. - self - assert: MCPToolRemoveClassSlot toolName - equals: 'class_slot_remove'. - self - assert: MCPToolUpdateClassSlotName toolName - equals: 'class_slot_name_update'. - self - assert: MCPToolPullUpClassSlot toolName - equals: 'class_slot_pull_up'. - self - assert: MCPToolPushDownClassSlot toolName - equals: 'class_slot_push_down'. + self assert: MCPToolRemoveClassSlot toolName equals: 'class_slot_remove'. + self assert: MCPToolUpdateClassSlotName toolName equals: 'class_slot_name_update'. + self assert: MCPToolPullUpClassSlot toolName equals: 'class_slot_pull_up'. + self assert: MCPToolPushDownClassSlot toolName equals: 'class_slot_push_down'. self assert: MCPToolRunTests toolName equals: 'test_run'. self assert: MCPToolCompileMethod toolName equals: 'method_compile'. - self - assert: MCPToolUpdateMethodSelector toolName - equals: 'method_selector_update'. - self - assert: MCPToolUpdateMethodProtocol toolName - equals: 'method_protocol_update'. - self - assert: MCPToolVerifyRepositoryIdentity toolName - equals: 'repository_identity_verify'. - self - assert: MCPToolListRepositoryChanges toolName - equals: 'repository_change_list'. - self - assert: MCPToolCreateRepository toolName - equals: 'repository_create'. - self - assert: MCPToolAttachRepository toolName - equals: 'repository_attach'. - self - assert: MCPToolUpdateRepository toolName - equals: 'repository_update'. - self - assert: MCPToolExportRepository toolName - equals: 'repository_export'. - self - assert: MCPToolCommitRepository toolName - equals: 'repository_commit'. - self - assert: MCPToolFetchRepository toolName - equals: 'repository_fetch'. + self assert: MCPToolUpdateMethodSelector toolName equals: 'method_selector_update'. + self assert: MCPToolUpdateMethodProtocol toolName equals: 'method_protocol_update'. + self assert: MCPToolVerifyRepositoryIdentity toolName equals: 'repository_identity_verify'. + self assert: MCPToolListRepositoryChanges toolName equals: 'repository_change_list'. + self assert: MCPToolCreateRepository toolName equals: 'repository_create'. + self assert: MCPToolAttachRepository toolName equals: 'repository_attach'. + self assert: MCPToolUpdateRepository toolName equals: 'repository_update'. + self assert: MCPToolExportRepository toolName equals: 'repository_export'. + self assert: MCPToolCommitRepository toolName equals: 'repository_commit'. + self assert: MCPToolFetchRepository toolName equals: 'repository_fetch'. self assert: MCPToolPullRepository toolName equals: 'repository_pull'. self assert: MCPToolPushRepository toolName equals: 'repository_push'. - self - assert: MCPToolCreateRepositoryBranch toolName - equals: 'repository_branch_create'. - self - assert: MCPToolSwitchRepositoryBranch toolName - equals: 'repository_branch_switch'. - self - assert: MCPToolAdoptRepositoryHead toolName - equals: 'repository_head_adopt'. - self - assert: MCPToolListChangeHistoryFiles toolName - equals: 'history_file_list'. - self - assert: MCPToolListChangeHistoryEntries toolName - equals: 'history_entry_list'. - self - assert: MCPToolApplyChangeHistoryEntries toolName - equals: 'history_entry_apply'. - self - assert: MCPToolRevertChangeHistoryEntries toolName - equals: 'history_entry_revert'. + self assert: MCPToolCreateRepositoryBranch toolName equals: 'repository_branch_create'. + self assert: MCPToolSwitchRepositoryBranch toolName equals: 'repository_branch_switch'. + self assert: MCPToolAdoptRepositoryHead toolName equals: 'repository_head_adopt'. + self assert: MCPToolListChangeHistoryFiles toolName equals: 'history_file_list'. + self assert: MCPToolListChangeHistoryEntries toolName equals: 'history_entry_list'. + self assert: MCPToolApplyChangeHistoryEntries toolName equals: 'history_entry_apply'. + self assert: MCPToolRevertChangeHistoryEntries toolName equals: 'history_entry_revert'. self assert: MCPToolDebugState toolName equals: 'debug_state_get'. - self - assert: MCPToolStepOverDebugSession toolName - equals: 'debug_session_step_over'. + self assert: MCPToolStepOverDebugSession toolName equals: 'debug_session_step_over'. self assert: MCPToolDebugCapture toolName equals: 'debug_capture'. - self - assert: MCPToolUpdateDebugMethod toolName - equals: 'debug_method_update' + self assert: MCPToolUpdateDebugMethod toolName equals: 'debug_method_update' +] + +{ #category : 'tests' } +MCPToolContractsTest >> testToolRegistryRegistrationIsInstanceScoped [ + + | firstRegistry secondRegistry | + firstRegistry := MCPToolRegistry new. + secondRegistry := MCPToolRegistry new. + firstRegistry registerToolClass: MCPHaltingTestTool. + self assert: (firstRegistry includesToolNamed: MCPHaltingTestTool toolName). + self deny: (secondRegistry includesToolNamed: MCPHaltingTestTool toolName) ] { #category : 'tests' } MCPToolContractsTest >> testToolRegistryReusesRegisteredToolInstances [ - | first searchResults second | - first := MCPToolRegistry - toolNamed: 'tool_search' - ifAbsent: [ self fail ]. - second := MCPToolRegistry - toolNamed: 'tool_search' - ifAbsent: [ self fail ]. - searchResults := MCPToolRegistry - toolsMatchingQuery: 'tool' - group: nil - usingPolicy: MCPToolExposurePolicy default. + | first registry searchResults second | + registry := MCPToolRegistry new. + first := registry toolNamed: 'tool_search' ifAbsent: [ self fail ]. + second := registry toolNamed: 'tool_search' ifAbsent: [ self fail ]. + searchResults := registry toolsMatchingQuery: 'tool' group: nil. self assert: first == second. - self - assert: (searchResults detect: [ :each | each name = 'tool_search' ]) - == first + self assert: (searchResults detect: [ :each | each name = 'tool_search' ]) == first +] + +{ #category : 'tests' } +MCPToolContractsTest >> testToolsOwnRegistryMetadata [ + + | tool | + tool := MCPToolEvaluate new. + self assert: tool groupName equals: 'runtime'. + self assert: tool defaultExposure equals: 'static'. + self assert: tool mutability equals: 'execute'. + self assert: tool risk equals: 'medium'. + self assert: (tool keywords includes: 'smalltalk') ] { #category : 'support' } diff --git a/src/MCP-Tests/MCPToolMethodLookupTest.class.st b/src/MCP-Tests/MCPToolMethodLookupTest.class.st index 9b2af4a..1ae1cb9 100644 --- a/src/MCP-Tests/MCPToolMethodLookupTest.class.st +++ b/src/MCP-Tests/MCPToolMethodLookupTest.class.st @@ -57,39 +57,24 @@ MCPToolMethodLookupTest >> testLookupSchemasRequireDedicatedArguments [ MCPToolMethodLookupTest >> testLookupToolsArePublicStaticReadTools [ | discoverableNames staticNames | - staticNames := #( 'method_source_search' 'method_implementor_search' - 'method_sender_search' ). - discoverableNames := #( 'method_equivalent_search' - 'method_class_reference_search' - 'method_variable_reference_search' ). + staticNames := #( 'method_source_search' 'method_implementor_search' 'method_sender_search' ). + discoverableNames := #( 'method_equivalent_search' 'method_class_reference_search' 'method_variable_reference_search' ). staticNames do: [ :each | | tool | - self assert: (MCPToolRegistry publicToolNames includes: each). - self assert: (MCPToolRegistry staticToolNames includes: each). - tool := MCPToolRegistry - toolNamed: each - ifAbsent: [ self fail: 'Missing tool ' , each ]. - self - assert: (MCPToolRegistry exposureForTool: tool) - equals: 'static'. - self - assert: (MCPToolRegistry mutabilityForTool: tool) - equals: 'read'. - self assert: (MCPToolRegistry riskForTool: tool) equals: 'low' ]. + self assert: (self toolRegistry publicToolNames includes: each). + self assert: (self toolRegistry staticToolNames includes: each). + tool := self toolRegistry toolNamed: each ifAbsent: [ self fail: 'Missing tool ' , each ]. + self assert: tool defaultExposure equals: 'static'. + self assert: tool mutability equals: 'read'. + self assert: tool risk equals: 'low' ]. discoverableNames do: [ :each | | tool | - self assert: (MCPToolRegistry publicToolNames includes: each). - self deny: (MCPToolRegistry staticToolNames includes: each). - tool := MCPToolRegistry - toolNamed: each - ifAbsent: [ self fail: 'Missing tool ' , each ]. - self - assert: (MCPToolRegistry exposureForTool: tool) - equals: 'discoverable'. - self - assert: (MCPToolRegistry mutabilityForTool: tool) - equals: 'read'. - self assert: (MCPToolRegistry riskForTool: tool) equals: 'low' ] + self assert: (self toolRegistry publicToolNames includes: each). + self deny: (self toolRegistry staticToolNames includes: each). + tool := self toolRegistry toolNamed: each ifAbsent: [ self fail: 'Missing tool ' , each ]. + self assert: tool defaultExposure equals: 'discoverable'. + self assert: tool mutability equals: 'read'. + self assert: tool risk equals: 'low' ] ] { #category : 'tests' } diff --git a/src/MCP-Tests/MCPToolRepositoryOperationTest.class.st b/src/MCP-Tests/MCPToolRepositoryOperationTest.class.st index c42f2d2..89537dd 100644 --- a/src/MCP-Tests/MCPToolRepositoryOperationTest.class.st +++ b/src/MCP-Tests/MCPToolRepositoryOperationTest.class.st @@ -13,9 +13,7 @@ Class { MCPToolRepositoryOperationTest >> callToolNamed: aToolName with: someArguments [ | request result tool | - tool := MCPToolRegistry - toolNamed: aToolName - ifAbsent: [ Error signal: 'Unknown MCP tool: ' , aToolName ]. + tool := MCPToolRegistry new toolNamed: aToolName ifAbsent: [ Error signal: 'Unknown MCP tool: ' , aToolName ]. request := tool requestFromToolCallArguments: someArguments. result := tool executeWithRequest: request. ^ result asJRPCJSON diff --git a/src/MCP-Tests/MCPToolRepositoryToolsTest.class.st b/src/MCP-Tests/MCPToolRepositoryToolsTest.class.st index 4579c36..71d698f 100644 --- a/src/MCP-Tests/MCPToolRepositoryToolsTest.class.st +++ b/src/MCP-Tests/MCPToolRepositoryToolsTest.class.st @@ -175,24 +175,15 @@ MCPToolRepositoryToolsTest >> testRepositoryToolsAreDiscoverableWithExpectedRisk | metadataByName | metadataByName := self metadataByNameForRepositoryTools. - #( 'repository_identity_verify' 'repository_change_list' - 'repository_create' 'repository_attach' 'repository_update' - 'repository_remote_add' 'repository_remote_remove' - 'repository_remote_update' 'repository_export' 'repository_commit' - 'repository_fetch' 'repository_pull' 'repository_push' - 'repository_branch_create' 'repository_branch_switch' + #( 'repository_identity_verify' 'repository_change_list' 'repository_create' 'repository_attach' 'repository_update' + 'repository_remote_add' 'repository_remote_remove' 'repository_remote_update' 'repository_export' 'repository_commit' + 'repository_fetch' 'repository_pull' 'repository_push' 'repository_branch_create' 'repository_branch_switch' 'repository_branch_checkout' 'repository_head_adopt' ) do: [ :toolName | | metadata tool | metadata := metadataByName at: toolName. - tool := MCPToolRegistry - toolNamed: toolName - ifAbsent: [ self fail: 'Missing tool ' , toolName ]. - self - assert: (MCPToolRegistry exposureForTool: tool) - equals: 'discoverable'. - self - assert: metadata keys asSet - equals: #( name description ) asSet ]. + tool := self toolRegistry toolNamed: toolName ifAbsent: [ self fail: 'Missing tool ' , toolName ]. + self assert: tool defaultExposure equals: 'discoverable'. + self assert: metadata keys asSet equals: #( name description ) asSet ]. self assert: (metadataByName includesKey: 'repository_load') ] diff --git a/src/MCP/MCP.class.st b/src/MCP/MCP.class.st index e1781fc..7278fde 100644 --- a/src/MCP/MCP.class.st +++ b/src/MCP/MCP.class.st @@ -293,8 +293,7 @@ MCP >> port: aPortNumber [ { #category : 'private - tools' } MCP >> refreshToolsList [ - toolsList := MCPToolRegistry staticToolsDictionaryUsingPolicy: - self toolExposurePolicy. + toolsList := self toolRegistry staticToolsDictionaryUsingPolicy: self toolExposurePolicy. ^ toolsList ] @@ -309,26 +308,15 @@ MCP >> restart [ MCP >> rpcCallToolCallWithParams: arguments [ | callRequest callTool request targetTool | - callTool := MCPToolRegistry - toolNamed: MCPToolCallTool toolName - ifAbsent: [ - Error signal: 'tool_call is not registered.' ] - usingPolicy: self toolExposurePolicy. + callTool := self toolRegistry toolNamed: MCPToolCallTool toolName ifAbsent: [ Error signal: 'tool_call is not registered.' ]. ^ [ request := callTool requestFromToolCallArguments: arguments. callRequest := callTool parsedRequestFromToolRequest: request. - callRequest toolName = callTool name ifTrue: [ - Error signal: 'tool_call cannot call itself.' ]. - targetTool := MCPToolRegistry + callRequest toolName = callTool name ifTrue: [ Error signal: 'tool_call cannot call itself.' ]. + targetTool := self toolRegistry toolNamed: callRequest toolName - ifAbsent: [ - Error signal: - 'Unknown MCP tool: ' , callRequest toolName ] - usingPolicy: self toolExposurePolicy. - self - executeTool: targetTool - named: targetTool name - withParams: callRequest arguments ] + ifAbsent: [ Error signal: 'Unknown MCP tool: ' , callRequest toolName ]. + self executeTool: targetTool named: targetTool name withParams: callRequest arguments ] on: Exception do: [ :error | | errorResult | @@ -484,6 +472,12 @@ MCP >> toolExposurePolicy: aToolExposurePolicy [ ^ toolExposurePolicy ] +{ #category : 'accessing' } +MCP >> toolRegistry [ + + ^ self toolExposurePolicy toolRegistry +] + { #category : 'accessing' } MCP >> toolsList [ diff --git a/src/MCP/MCPGetToolCommand.class.st b/src/MCP/MCPGetToolCommand.class.st index e41bbe0..09f407a 100644 --- a/src/MCP/MCPGetToolCommand.class.st +++ b/src/MCP/MCPGetToolCommand.class.st @@ -24,10 +24,8 @@ MCPGetToolCommand class >> tool: aTool request: aRequest [ MCPGetToolCommand >> execute [ | catalogTool | - catalogTool := MCPToolRegistry + catalogTool := self toolExposurePolicy toolRegistry toolNamed: request toolName - ifAbsent: [ Error signal: 'Unknown MCP tool: ' , request toolName ] - usingPolicy: self toolExposurePolicy. - ^ { (#tool -> (MCPToolRegistry contractForTool: catalogTool usingPolicy: self toolExposurePolicy includeOutputSchema: false)) } - asDictionary + ifAbsent: [ Error signal: 'Unknown MCP tool: ' , request toolName ]. + ^ { (#tool -> (catalogTool contractUsingPolicy: self toolExposurePolicy includeOutputSchema: false)) } asDictionary ] diff --git a/src/MCP/MCPSearchToolsCommand.class.st b/src/MCP/MCPSearchToolsCommand.class.st index b0239fc..7dfbd78 100644 --- a/src/MCP/MCPSearchToolsCommand.class.st +++ b/src/MCP/MCPSearchToolsCommand.class.st @@ -25,20 +25,11 @@ MCPSearchToolsCommand >> execute [ | data matchingTools pageSize visibleTools | pageSize := self pageSize. - matchingTools := MCPToolRegistry - toolsMatchingQuery: request query - group: request group - usingPolicy: self toolExposurePolicy. + matchingTools := self toolExposurePolicy toolRegistry toolsMatchingQuery: request query group: request group. visibleTools := self visibleToolsFrom: matchingTools. data := Dictionary new. - data - at: #tools - put: ((visibleTools collect: [ :each | - MCPToolRegistry - searchMetadataForTool: each - usingPolicy: self toolExposurePolicy ]) asArray). - (self hasMoreToolsAfter: matchingTools) ifTrue: [ - data at: #nextCursor put: (request cursor + pageSize) asString ]. + data at: #tools put: (visibleTools collect: [ :each | each searchMetadata ]) asArray. + (self hasMoreToolsAfter: matchingTools) ifTrue: [ data at: #nextCursor put: (request cursor + pageSize) asString ]. ^ data ] diff --git a/src/MCP/MCPTool.class.st b/src/MCP/MCPTool.class.st index c082a82..efbfd44 100644 --- a/src/MCP/MCPTool.class.st +++ b/src/MCP/MCPTool.class.st @@ -38,10 +38,16 @@ MCPTool class >> isAbstract [ ^ self = MCPTool ] +{ #category : 'metadata' } +MCPTool class >> isBuiltIn [ + + ^ self package = MCPTool package +] + { #category : 'scripts' } MCPTool class >> toolGroups [ - ^ MCPToolRegistry publicToolGroups + ^ MCPToolRegistry new publicToolGroups ] { #category : 'scripts' } @@ -69,6 +75,18 @@ MCPTool class >> toolName [ self subclassResponsibility ] +{ #category : 'metadata' } +MCPTool >> activationRequirements [ + + ^ #( ) +] + +{ #category : 'metadata' } +MCPTool >> additionalKeywords [ + + ^ #( ) +] + { #category : 'accessing' } MCPTool >> asJRPCJSON [ @@ -120,6 +138,27 @@ MCPTool >> commandForRequest: aToolRequest [ self subclassResponsibility ] +{ #category : 'metadata' } +MCPTool >> contractUsingPolicy: aToolExposurePolicy includeOutputSchema: includeOutputSchema [ + + | activationRequirements data examples | + data := self metadata copy. + data at: #exposure put: (aToolExposurePolicy exposureForTool: self). + data at: #inputSchema put: self inputSchema asJRPCJSON. + includeOutputSchema ifTrue: [ data at: #outputSchema put: self outputSchema asJRPCJSON ]. + examples := self examples. + examples ifNotEmpty: [ data at: #examples put: examples ]. + activationRequirements := self activationRequirements. + activationRequirements ifNotEmpty: [ data at: #activationRequirements put: activationRequirements ]. + ^ data +] + +{ #category : 'metadata' } +MCPTool >> defaultExposure [ + + ^ 'discoverable' +] + { #category : 'metadata' } MCPTool >> description [ @@ -224,6 +263,12 @@ MCPTool >> errorStackTraceLimit [ ^ 20 ] +{ #category : 'metadata' } +MCPTool >> examples [ + + ^ #( ) +] + { #category : 'private - execution' } MCPTool >> executeParsedRequestFrom: request do: executionBlock onError: errorBlock [ "Parse the transport request inside the same exception boundary as execution. @@ -269,6 +314,12 @@ MCPTool >> failureMessageFor: anError [ nextPutAll: message ] ] ] +{ #category : 'metadata' } +MCPTool >> groupName [ + + ^ 'other' +] + { #category : 'metadata' } MCPTool >> icons [ @@ -300,6 +351,38 @@ MCPTool >> integerSchemaPropertyNamed: propertyName description: propertyDescrip yourself ] +{ #category : 'metadata' } +MCPTool >> keywords [ + + | keywords normalizedKeywords | + keywords := OrderedCollection new. + keywords addAll: (self name findTokens: '-_'). + keywords addAll: (self title findTokens: ' -'). + keywords addAll: (self groupName findTokens: '-_'). + keywords addAll: self additionalKeywords. + normalizedKeywords := OrderedCollection new. + keywords do: [ :each | normalizedKeywords addAll: (each asString findTokens: '-_ ') ]. + ^ (normalizedKeywords collect: [ :each | each asLowercase ]) asSet asArray +] + +{ #category : 'metadata' } +MCPTool >> metadata [ + + ^ { + (#name -> self name). + (#description -> self description). + (#group -> self groupName). + (#mutability -> self mutability). + (#risk -> self risk). + (#keywords -> self keywords) } asDictionary +] + +{ #category : 'metadata' } +MCPTool >> mutability [ + + ^ 'write' +] + { #category : 'metadata' } MCPTool >> name [ @@ -350,6 +433,12 @@ MCPTool >> requestFromToolCallArguments: toolCallArguments [ ^ MCPToolRequest tool: self arguments: toolCallArguments ] +{ #category : 'metadata' } +MCPTool >> risk [ + + ^ 'high' +] + { #category : 'private - schema' } MCPTool >> schemaPropertyNamed: aName type: aType description: aDescription [ @@ -360,6 +449,32 @@ MCPTool >> schemaPropertyNamed: aName type: aType description: aDescription [ yourself ] +{ #category : 'metadata' } +MCPTool >> searchMetadata [ + + ^ { + (#name -> self name). + (#description -> self description) } asDictionary +] + +{ #category : 'metadata' } +MCPTool >> searchableText [ + + ^ String streamContents: [ :stream | + stream + nextPutAll: self name; + space; + nextPutAll: self title; + space; + nextPutAll: self description; + space; + nextPutAll: self groupName. + self keywords do: [ :keyword | + stream + space; + nextPutAll: keyword ] ] +] + { #category : 'private - schema' } MCPTool >> shouldAdvertiseStringArrayItemDescription: anItemDescription [ diff --git a/src/MCP/MCPToolCallTool.class.st b/src/MCP/MCPToolCallTool.class.st index fac3861..7c74174 100644 --- a/src/MCP/MCPToolCallTool.class.st +++ b/src/MCP/MCPToolCallTool.class.st @@ -68,6 +68,12 @@ MCPToolCallTool >> commandForRequest: callRequest [ ^ MCPCallToolCommand tool: self request: callRequest ] +{ #category : 'metadata' } +MCPToolCallTool >> defaultExposure [ + + ^ 'static' +] + { #category : 'metadata' } MCPToolCallTool >> description [ @@ -86,12 +92,30 @@ MCPToolCallTool >> executeWithRequest: request [ onError: [ :error :parsedRequest | self errorResultFor: error ] ] +{ #category : 'metadata' } +MCPToolCallTool >> groupName [ + + ^ 'tool-catalog' +] + +{ #category : 'metadata' } +MCPToolCallTool >> mutability [ + + ^ 'execute' +] + { #category : 'private - request' } MCPToolCallTool >> parsedRequestFromToolRequest: request [ ^ MCPCallToolRequest fromToolRequest: request ] +{ #category : 'metadata' } +MCPToolCallTool >> risk [ + + ^ 'medium' +] + { #category : 'metadata' } MCPToolCallTool >> title [ diff --git a/src/MCP/MCPToolCaptureScreenshot.class.st b/src/MCP/MCPToolCaptureScreenshot.class.st index 2ac3607..63274d4 100644 --- a/src/MCP/MCPToolCaptureScreenshot.class.st +++ b/src/MCP/MCPToolCaptureScreenshot.class.st @@ -17,6 +17,12 @@ MCPToolCaptureScreenshot class >> toolName [ ^ 'screenshot_capture' ] +{ #category : 'metadata' } +MCPToolCaptureScreenshot >> additionalKeywords [ + + ^ #( 'screenshot' 'capture' 'window' 'display' 'world' 'ui' ) +] + { #category : 'metadata' } MCPToolCaptureScreenshot >> buildInputSchema [ @@ -90,12 +96,30 @@ MCPToolCaptureScreenshot >> executeWithRequest: request [ onError: [ :error :ignored | self errorResultText: (self failureMessageFor: error) details: (self errorDetailsFor: error) ] ] +{ #category : 'metadata' } +MCPToolCaptureScreenshot >> groupName [ + + ^ 'ui' +] + +{ #category : 'metadata' } +MCPToolCaptureScreenshot >> mutability [ + + ^ 'read' +] + { #category : 'private - request' } MCPToolCaptureScreenshot >> parsedRequestFromToolRequest: request [ ^ MCPScreenshotRequest fromRequest: request tool: self ] +{ #category : 'metadata' } +MCPToolCaptureScreenshot >> risk [ + + ^ 'low' +] + { #category : 'private - results' } MCPToolCaptureScreenshot >> successSummaryForResult: aScreenshotResult [ diff --git a/src/MCP/MCPToolChangeHistory.class.st b/src/MCP/MCPToolChangeHistory.class.st index f157e3b..b156062 100644 --- a/src/MCP/MCPToolChangeHistory.class.st +++ b/src/MCP/MCPToolChangeHistory.class.st @@ -17,6 +17,12 @@ MCPToolChangeHistory class >> isAbstract [ ^ self = MCPToolChangeHistory ] +{ #category : 'metadata' } +MCPToolChangeHistory >> additionalKeywords [ + + ^ #( 'history' 'change' 'epicea' 'ombu' 'recover' ) +] + { #category : 'private - schema' } MCPToolChangeHistory >> changeHistoryEntryArraySchemaNamed: propertyName description: propertyDescription [ @@ -164,6 +170,12 @@ MCPToolChangeHistory >> executeWithRequest: request [ otherwise: [ :toolError | self errorResultFor: toolError ] ] ] +{ #category : 'metadata' } +MCPToolChangeHistory >> groupName [ + + ^ 'history' +] + { #category : 'private - schema' } MCPToolChangeHistory >> integerArraySchemaNamed: aName description: aDescription itemDescription: anItemDescription [ diff --git a/src/MCP/MCPToolCheckoutRepositoryBranch.class.st b/src/MCP/MCPToolCheckoutRepositoryBranch.class.st index b54b937..a468986 100644 --- a/src/MCP/MCPToolCheckoutRepositoryBranch.class.st +++ b/src/MCP/MCPToolCheckoutRepositoryBranch.class.st @@ -15,6 +15,12 @@ MCPToolCheckoutRepositoryBranch class >> toolName [ ^ 'repository_branch_checkout' ] +{ #category : 'metadata' } +MCPToolCheckoutRepositoryBranch >> additionalKeywords [ + + ^ super additionalKeywords , #( 'checkout' 'reload' 'stale' 'verification' ) +] + { #category : 'private - specs' } MCPToolCheckoutRepositoryBranch >> repositoryToolSpec [ diff --git a/src/MCP/MCPToolClassMutation.class.st b/src/MCP/MCPToolClassMutation.class.st index cd977e1..212b263 100644 --- a/src/MCP/MCPToolClassMutation.class.st +++ b/src/MCP/MCPToolClassMutation.class.st @@ -19,6 +19,12 @@ MCPToolClassMutation class >> isAbstract [ ^ self = MCPToolClassMutation ] +{ #category : 'metadata' } +MCPToolClassMutation >> additionalKeywords [ + + ^ #( 'class' 'refactor' 'refactoring' ) +] + { #category : 'private - schema' } MCPToolClassMutation >> atLeastOneInputProperties [ @@ -292,6 +298,12 @@ MCPToolClassMutation >> fixedArguments [ ^ self classToolSpec at: #fixedArguments ifAbsent: [ Dictionary new ] ] +{ #category : 'metadata' } +MCPToolClassMutation >> groupName [ + + ^ 'classes' +] + { #category : 'private - schema' } MCPToolClassMutation >> inputProperties [ diff --git a/src/MCP/MCPToolCompileMethod.class.st b/src/MCP/MCPToolCompileMethod.class.st index c204f73..12aa0f1 100644 --- a/src/MCP/MCPToolCompileMethod.class.st +++ b/src/MCP/MCPToolCompileMethod.class.st @@ -17,6 +17,12 @@ MCPToolCompileMethod class >> toolName [ ^ 'method_compile' ] +{ #category : 'metadata' } +MCPToolCompileMethod >> defaultExposure [ + + ^ 'static' +] + { #category : 'private - specs' } MCPToolCompileMethod >> methodToolSpec [ diff --git a/src/MCP/MCPToolCreateClass.class.st b/src/MCP/MCPToolCreateClass.class.st index e1bc1d8..e2e5f5f 100644 --- a/src/MCP/MCPToolCreateClass.class.st +++ b/src/MCP/MCPToolCreateClass.class.st @@ -40,3 +40,9 @@ MCPToolCreateClass >> classToolSpec [ -> #( 'className' 'superclassName' 'packageName' )) } asDictionary ] + +{ #category : 'metadata' } +MCPToolCreateClass >> defaultExposure [ + + ^ 'static' +] diff --git a/src/MCP/MCPToolDebug.class.st b/src/MCP/MCPToolDebug.class.st index a3d1b9d..4c21b01 100644 --- a/src/MCP/MCPToolDebug.class.st +++ b/src/MCP/MCPToolDebug.class.st @@ -17,6 +17,12 @@ MCPToolDebug class >> isAbstract [ ^ self = MCPToolDebug ] +{ #category : 'metadata' } +MCPToolDebug >> additionalKeywords [ + + ^ #( 'debug' 'session' 'state' 'frame' 'breakpoint' 'exception' 'repair' 'control' ) +] + { #category : 'private - schema' } MCPToolDebug >> boundedIntegerSchemaPropertyNamed: propertyName description: propertyDescription default: defaultValue maximum: maximumValue [ @@ -76,6 +82,12 @@ MCPToolDebug >> executeWithRequest: request [ onError: [ :error :ignored | self errorResultForDebugError: error ] ] +{ #category : 'metadata' } +MCPToolDebug >> groupName [ + + ^ 'debugging' +] + { #category : 'private - schema' } MCPToolDebug >> inputProperties [ diff --git a/src/MCP/MCPToolDebugCapture.class.st b/src/MCP/MCPToolDebugCapture.class.st index eda0ea0..46a8238 100644 --- a/src/MCP/MCPToolDebugCapture.class.st +++ b/src/MCP/MCPToolDebugCapture.class.st @@ -139,12 +139,24 @@ MCPToolDebugCapture >> description [ ^ 'Evaluate Smalltalk in a bounded worker process and capture runtime exceptions as tracked debug sessions.' ] +{ #category : 'metadata' } +MCPToolDebugCapture >> mutability [ + + ^ 'execute' +] + { #category : 'private - request' } MCPToolDebugCapture >> parsedRequestFromToolRequest: request [ ^ MCPDebugCaptureRequest fromRequest: request tool: self ] +{ #category : 'metadata' } +MCPToolDebugCapture >> risk [ + + ^ 'medium' +] + { #category : 'testing' } MCPToolDebugCapture >> shouldSaveImageAfterSuccessfulExecution [ diff --git a/src/MCP/MCPToolDebugEvaluate.class.st b/src/MCP/MCPToolDebugEvaluate.class.st index d41c4ce..35e3ee1 100644 --- a/src/MCP/MCPToolDebugEvaluate.class.st +++ b/src/MCP/MCPToolDebugEvaluate.class.st @@ -122,6 +122,12 @@ MCPToolDebugEvaluate >> description [ ^ 'Evaluate a Smalltalk expression in a selected DebugSession frame. The expression resolves frame receiver and temporaries and may mutate image state.' ] +{ #category : 'metadata' } +MCPToolDebugEvaluate >> mutability [ + + ^ 'execute' +] + { #category : 'private - request' } MCPToolDebugEvaluate >> parsedRequestFromToolRequest: request [ @@ -134,6 +140,12 @@ MCPToolDebugEvaluate >> resultPreviewCharacterLimit [ ^ 10000 ] +{ #category : 'metadata' } +MCPToolDebugEvaluate >> risk [ + + ^ 'medium' +] + { #category : 'private' } MCPToolDebugEvaluate >> runtimeFailureMessageFor: anError [ diff --git a/src/MCP/MCPToolDebugState.class.st b/src/MCP/MCPToolDebugState.class.st index 05e4998..1526dbf 100644 --- a/src/MCP/MCPToolDebugState.class.st +++ b/src/MCP/MCPToolDebugState.class.st @@ -151,12 +151,24 @@ MCPToolDebugState >> description [ ^ 'Return a compact snapshot of one tracked Pharo debug session: selected frame, source, stack, shallow scopes, and repair actions.' ] +{ #category : 'metadata' } +MCPToolDebugState >> mutability [ + + ^ 'read' +] + { #category : 'private - request' } MCPToolDebugState >> parsedRequestFromToolRequest: request [ ^ MCPDebugStateRequest fromRequest: request tool: self ] +{ #category : 'metadata' } +MCPToolDebugState >> risk [ + + ^ 'low' +] + { #category : 'metadata' } MCPToolDebugState >> title [ diff --git a/src/MCP/MCPToolDebugTest.class.st b/src/MCP/MCPToolDebugTest.class.st index 87dd483..fc4aac2 100644 --- a/src/MCP/MCPToolDebugTest.class.st +++ b/src/MCP/MCPToolDebugTest.class.st @@ -154,12 +154,24 @@ MCPToolDebugTest >> description [ ^ 'Run one SUnit test method under debugger control and stop at the requested phase.' ] +{ #category : 'metadata' } +MCPToolDebugTest >> mutability [ + + ^ 'execute' +] + { #category : 'private - request' } MCPToolDebugTest >> parsedRequestFromToolRequest: request [ ^ MCPDebugTestRequest fromRequest: request tool: self ] +{ #category : 'metadata' } +MCPToolDebugTest >> risk [ + + ^ 'medium' +] + { #category : 'testing' } MCPToolDebugTest >> shouldSaveImageAfterSuccessfulExecution [ diff --git a/src/MCP/MCPToolDebugVariables.class.st b/src/MCP/MCPToolDebugVariables.class.st index d11a646..59e0e06 100644 --- a/src/MCP/MCPToolDebugVariables.class.st +++ b/src/MCP/MCPToolDebugVariables.class.st @@ -127,12 +127,24 @@ MCPToolDebugVariables >> description [ ^ 'Expand one state-scoped variable or scope reference returned by debug_state_get.' ] +{ #category : 'metadata' } +MCPToolDebugVariables >> mutability [ + + ^ 'read' +] + { #category : 'private - request' } MCPToolDebugVariables >> parsedRequestFromToolRequest: request [ ^ MCPDebugVariablesRequest fromRequest: request tool: self ] +{ #category : 'metadata' } +MCPToolDebugVariables >> risk [ + + ^ 'low' +] + { #category : 'metadata' } MCPToolDebugVariables >> title [ diff --git a/src/MCP/MCPToolEvaluate.class.st b/src/MCP/MCPToolEvaluate.class.st index 4cd6c1d..767906c 100644 --- a/src/MCP/MCPToolEvaluate.class.st +++ b/src/MCP/MCPToolEvaluate.class.st @@ -15,6 +15,12 @@ MCPToolEvaluate class >> toolName [ ^ 'image_evaluate' ] +{ #category : 'metadata' } +MCPToolEvaluate >> additionalKeywords [ + + ^ #( 'expression' 'smalltalk' ) +] + { #category : 'metadata' } MCPToolEvaluate >> buildInputSchema [ @@ -58,6 +64,12 @@ MCPToolEvaluate >> commandForRequest: evaluateRequest [ resultPreviewCharacterLimit: self resultPreviewCharacterLimit ] +{ #category : 'metadata' } +MCPToolEvaluate >> defaultExposure [ + + ^ 'static' +] + { #category : 'metadata' } MCPToolEvaluate >> description [ @@ -90,6 +102,18 @@ MCPToolEvaluate >> executeWithRequest: request [ details: (self errorDetailsFor: error) ] ] +{ #category : 'metadata' } +MCPToolEvaluate >> groupName [ + + ^ 'runtime' +] + +{ #category : 'metadata' } +MCPToolEvaluate >> mutability [ + + ^ 'execute' +] + { #category : 'private - request' } MCPToolEvaluate >> parsedRequestFromToolRequest: request [ @@ -102,6 +126,12 @@ MCPToolEvaluate >> resultPreviewCharacterLimit [ ^ 10000 ] +{ #category : 'metadata' } +MCPToolEvaluate >> risk [ + + ^ 'medium' +] + { #category : 'private' } MCPToolEvaluate >> runtimeFailureMessageFor: anError [ diff --git a/src/MCP/MCPToolExposurePolicy.class.st b/src/MCP/MCPToolExposurePolicy.class.st index a13df88..7572801 100644 --- a/src/MCP/MCPToolExposurePolicy.class.st +++ b/src/MCP/MCPToolExposurePolicy.class.st @@ -1,13 +1,12 @@ " -I decide which registered MCP tools are advertised directly through tools/list for one MCP server instance. - -The default policy follows each tool registration's default exposure. Supplying staticToolNames: switches to an explicit static surface; all other registered tools remain discoverable through tool_call. +Per-server visibility policy for one MCPToolRegistry. By default it respects each tool's default exposure; users can replace the static tool-name set without changing tool metadata or another registry. " Class { #name : 'MCPToolExposurePolicy', #superclass : 'Object', #instVars : [ - 'staticToolNames' + 'staticToolNames', + 'toolRegistry' ], #category : 'MCP-Tools', #package : 'MCP', @@ -17,19 +16,28 @@ Class { { #category : 'instance creation' } MCPToolExposurePolicy class >> allStatic [ - ^ self staticToolNames: MCPToolRegistry publicToolNames + | policy | + policy := self default. + policy staticToolNames: policy toolRegistry publicToolNames. + ^ policy ] { #category : 'instance creation' } MCPToolExposurePolicy class >> default [ - ^ self new + ^ self forRegistry: MCPToolRegistry new +] + +{ #category : 'instance creation' } +MCPToolExposurePolicy class >> forRegistry: aToolRegistry [ + + ^ self new initializeToolRegistry: aToolRegistry ] { #category : 'instance creation' } MCPToolExposurePolicy class >> staticToolNames: aCollection [ - ^ self new + ^ self default staticToolNames: aCollection; yourself ] @@ -37,9 +45,7 @@ MCPToolExposurePolicy class >> staticToolNames: aCollection [ { #category : 'tools' } MCPToolExposurePolicy >> exposureForTool: aTool [ - ^ self exposureForToolRegistration: (MCPToolRegistry - registrationForTool: aTool - ifAbsent: [ ^ 'discoverable' ]) + ^ self exposureForToolRegistration: (self toolRegistry registrationForTool: aTool ifAbsent: [ ^ 'discoverable' ]) ] { #category : 'tools' } @@ -56,13 +62,16 @@ MCPToolExposurePolicy >> hasExplicitStaticToolNames [ ^ staticToolNames notNil ] +{ #category : 'initialization' } +MCPToolExposurePolicy >> initializeToolRegistry: aToolRegistry [ + + toolRegistry := aToolRegistry +] + { #category : 'testing' } MCPToolExposurePolicy >> isStaticTool: aToolName [ - ^ self isStaticToolRegistration: - (MCPToolRegistry - registrationNamed: aToolName - ifAbsent: [ ^ false ]) + ^ self isStaticToolRegistration: (self toolRegistry registrationNamed: aToolName ifAbsent: [ ^ false ]) ] { #category : 'testing' } @@ -76,19 +85,21 @@ MCPToolExposurePolicy >> isStaticToolRegistration: aToolRegistration [ { #category : 'accessing' } MCPToolExposurePolicy >> staticToolNames [ - ^ staticToolNames ifNil: [ MCPToolRegistry defaultStaticToolNames ] + ^ staticToolNames ifNil: [ self toolRegistry staticToolNames ] ] { #category : 'accessing' } MCPToolExposurePolicy >> staticToolNames: aCollection [ | normalized unknown | - normalized := (aCollection collect: [ :each | each asString ]) - asArray. - unknown := normalized reject: [ :each | - MCPToolRegistry includesToolNamed: each ]. - unknown ifNotEmpty: [ - Error signal: - 'Unknown MCP tool(s) in exposure policy: ' , unknown printString ]. + normalized := (aCollection collect: [ :each | each asString ]) asArray. + unknown := normalized reject: [ :each | self toolRegistry includesToolNamed: each ]. + unknown ifNotEmpty: [ Error signal: 'Unknown MCP tool(s) in exposure policy: ' , unknown printString ]. staticToolNames := normalized ] + +{ #category : 'accessing' } +MCPToolExposurePolicy >> toolRegistry [ + + ^ toolRegistry ifNil: [ toolRegistry := MCPToolRegistry new ] +] diff --git a/src/MCP/MCPToolGetClass.class.st b/src/MCP/MCPToolGetClass.class.st index 4c9e98a..b63a5f3 100644 --- a/src/MCP/MCPToolGetClass.class.st +++ b/src/MCP/MCPToolGetClass.class.st @@ -200,6 +200,12 @@ MCPToolGetClass >> commandForRequest: getRequest [ ^ MCPGetClassCommand tool: self request: getRequest ] +{ #category : 'metadata' } +MCPToolGetClass >> defaultExposure [ + + ^ 'static' +] + { #category : 'metadata' } MCPToolGetClass >> description [ @@ -276,18 +282,36 @@ MCPToolGetClass >> failureMessageForClassName: className error: anError [ nextPutAll: anError messageText ] ] +{ #category : 'metadata' } +MCPToolGetClass >> groupName [ + + ^ 'classes' +] + { #category : 'defaults' } MCPToolGetClass >> maximumSubclassDepth [ ^ 3 ] +{ #category : 'metadata' } +MCPToolGetClass >> mutability [ + + ^ 'read' +] + { #category : 'private - request' } MCPToolGetClass >> parsedRequestFromToolRequest: request [ ^ MCPGetClassRequest fromRequest: request tool: self ] +{ #category : 'metadata' } +MCPToolGetClass >> risk [ + + ^ 'low' +] + { #category : 'private' } MCPToolGetClass >> successSummaryForClassName: className superclasses: superclasses subclasses: subclasses [ diff --git a/src/MCP/MCPToolGetDebugSession.class.st b/src/MCP/MCPToolGetDebugSession.class.st index c611c1d..cf96614 100644 --- a/src/MCP/MCPToolGetDebugSession.class.st +++ b/src/MCP/MCPToolGetDebugSession.class.st @@ -15,6 +15,18 @@ MCPToolGetDebugSession class >> toolName [ ^ 'debug_session_get' ] +{ #category : 'metadata' } +MCPToolGetDebugSession >> mutability [ + + ^ 'read' +] + +{ #category : 'metadata' } +MCPToolGetDebugSession >> risk [ + + ^ 'low' +] + { #category : 'private - specs' } MCPToolGetDebugSession >> sessionToolSpec [ diff --git a/src/MCP/MCPToolGetMethod.class.st b/src/MCP/MCPToolGetMethod.class.st index 215dc33..77a1e04 100644 --- a/src/MCP/MCPToolGetMethod.class.st +++ b/src/MCP/MCPToolGetMethod.class.st @@ -105,6 +105,12 @@ MCPToolGetMethod >> commandForRequest: getRequest [ ^ MCPGetMethodCommand tool: self request: getRequest ] +{ #category : 'metadata' } +MCPToolGetMethod >> defaultExposure [ + + ^ 'static' +] + { #category : 'private - analysis' } MCPToolGetMethod >> definingClassNameFromBinding: aBinding [ @@ -191,6 +197,12 @@ MCPToolGetMethod >> failureMessageForClassNamed: className selector: selectorStr nextPutAll: anError messageText ] ] +{ #category : 'metadata' } +MCPToolGetMethod >> groupName [ + + ^ 'methods' +] + { #category : 'private - analysis' } MCPToolGetMethod >> incrementReferenceCountInSummary: aSummary [ @@ -208,6 +220,12 @@ MCPToolGetMethod >> matchingSummaryForNodeName: aName kind: aKind ownerName: anO ifNone: [ nil ] ] +{ #category : 'metadata' } +MCPToolGetMethod >> mutability [ + + ^ 'read' +] + { #category : 'private - analysis' } MCPToolGetMethod >> newVariableSummaryForNode: aVariableNode kind: aKind methodBehavior: aBehavior [ @@ -254,6 +272,12 @@ MCPToolGetMethod >> parsedRequestFromToolRequest: request [ ^ MCPGetMethodRequest fromRequest: request tool: self ] +{ #category : 'metadata' } +MCPToolGetMethod >> risk [ + + ^ 'low' +] + { #category : 'private - errors' } MCPToolGetMethod >> selectorForErrorFromParsedRequest: getRequest rawRequest: rawRequest [ diff --git a/src/MCP/MCPToolGetTool.class.st b/src/MCP/MCPToolGetTool.class.st index 972c87d..18f6ed7 100644 --- a/src/MCP/MCPToolGetTool.class.st +++ b/src/MCP/MCPToolGetTool.class.st @@ -94,6 +94,12 @@ MCPToolGetTool >> commandForRequest: getRequest [ ^ MCPGetToolCommand tool: self request: getRequest ] +{ #category : 'metadata' } +MCPToolGetTool >> defaultExposure [ + + ^ 'static' +] + { #category : 'metadata' } MCPToolGetTool >> description [ @@ -126,12 +132,30 @@ MCPToolGetTool >> executeWithRequest: request usingPolicy: aToolExposurePolicy [ onError: [ :error :parsedRequest | self errorResultFor: error ] ] +{ #category : 'metadata' } +MCPToolGetTool >> groupName [ + + ^ 'tool-catalog' +] + +{ #category : 'metadata' } +MCPToolGetTool >> mutability [ + + ^ 'read' +] + { #category : 'private - request' } MCPToolGetTool >> parsedRequestFromToolRequest: request [ ^ MCPGetToolRequest fromToolRequest: request ] +{ #category : 'metadata' } +MCPToolGetTool >> risk [ + + ^ 'low' +] + { #category : 'metadata' } MCPToolGetTool >> title [ diff --git a/src/MCP/MCPToolListChangeHistoryEntries.class.st b/src/MCP/MCPToolListChangeHistoryEntries.class.st index 83d2008..eefdedd 100644 --- a/src/MCP/MCPToolListChangeHistoryEntries.class.st +++ b/src/MCP/MCPToolListChangeHistoryEntries.class.st @@ -91,12 +91,24 @@ MCPToolListChangeHistoryEntries >> description [ ^ 'List granular entries from the current Pharo image change history or a selected .ombu file. Use history_file_list to locate files, and history_entry_apply or history_entry_revert only when previewing or performing recovery changes.' ] +{ #category : 'metadata' } +MCPToolListChangeHistoryEntries >> mutability [ + + ^ 'read' +] + { #category : 'private - request' } MCPToolListChangeHistoryEntries >> parsedRequestFromToolRequest: request [ ^ MCPChangeHistoryListEntriesRequest fromRequest: request tool: self ] +{ #category : 'metadata' } +MCPToolListChangeHistoryEntries >> risk [ + + ^ 'low' +] + { #category : 'metadata' } MCPToolListChangeHistoryEntries >> title [ diff --git a/src/MCP/MCPToolListChangeHistoryFiles.class.st b/src/MCP/MCPToolListChangeHistoryFiles.class.st index 9faee14..6bf92ad 100644 --- a/src/MCP/MCPToolListChangeHistoryFiles.class.st +++ b/src/MCP/MCPToolListChangeHistoryFiles.class.st @@ -86,12 +86,24 @@ MCPToolListChangeHistoryFiles >> maximumFileLimit [ ^ 100 ] +{ #category : 'metadata' } +MCPToolListChangeHistoryFiles >> mutability [ + + ^ 'read' +] + { #category : 'private - request' } MCPToolListChangeHistoryFiles >> parsedRequestFromToolRequest: request [ ^ MCPChangeHistoryListLogsRequest fromRequest: request tool: self ] +{ #category : 'metadata' } +MCPToolListChangeHistoryFiles >> risk [ + + ^ 'low' +] + { #category : 'metadata' } MCPToolListChangeHistoryFiles >> title [ diff --git a/src/MCP/MCPToolListDebugBreakpoints.class.st b/src/MCP/MCPToolListDebugBreakpoints.class.st index 0955137..fd350a2 100644 --- a/src/MCP/MCPToolListDebugBreakpoints.class.st +++ b/src/MCP/MCPToolListDebugBreakpoints.class.st @@ -24,3 +24,15 @@ MCPToolListDebugBreakpoints >> breakpointToolSpec [ -> 'List tracked transient Pharo DebugPoint breakpoints.'). (#action -> 'list') } asDictionary ] + +{ #category : 'metadata' } +MCPToolListDebugBreakpoints >> mutability [ + + ^ 'read' +] + +{ #category : 'metadata' } +MCPToolListDebugBreakpoints >> risk [ + + ^ 'low' +] diff --git a/src/MCP/MCPToolListDebugSessions.class.st b/src/MCP/MCPToolListDebugSessions.class.st index 534c71a..fc0a058 100644 --- a/src/MCP/MCPToolListDebugSessions.class.st +++ b/src/MCP/MCPToolListDebugSessions.class.st @@ -15,6 +15,18 @@ MCPToolListDebugSessions class >> toolName [ ^ 'debug_session_list' ] +{ #category : 'metadata' } +MCPToolListDebugSessions >> mutability [ + + ^ 'read' +] + +{ #category : 'metadata' } +MCPToolListDebugSessions >> risk [ + + ^ 'low' +] + { #category : 'private - specs' } MCPToolListDebugSessions >> sessionToolSpec [ diff --git a/src/MCP/MCPToolListRepositoryChanges.class.st b/src/MCP/MCPToolListRepositoryChanges.class.st index f816180..73ee73a 100644 --- a/src/MCP/MCPToolListRepositoryChanges.class.st +++ b/src/MCP/MCPToolListRepositoryChanges.class.st @@ -15,6 +15,18 @@ MCPToolListRepositoryChanges class >> toolName [ ^ 'repository_change_list' ] +{ #category : 'metadata' } +MCPToolListRepositoryChanges >> additionalKeywords [ + + ^ super additionalKeywords , #( 'diff' 'status' 'change' ) +] + +{ #category : 'metadata' } +MCPToolListRepositoryChanges >> mutability [ + + ^ 'read' +] + { #category : 'private - specs' } MCPToolListRepositoryChanges >> repositoryToolSpec [ @@ -27,3 +39,9 @@ MCPToolListRepositoryChanges >> repositoryToolSpec [ (#requestClass -> MCPRepositoryDiffRequest). (#commandClass -> MCPRepositoryDiffCommand) } asDictionary ] + +{ #category : 'metadata' } +MCPToolListRepositoryChanges >> risk [ + + ^ 'low' +] diff --git a/src/MCP/MCPToolLoadBaseline.class.st b/src/MCP/MCPToolLoadBaseline.class.st index 3674593..816b007 100644 --- a/src/MCP/MCPToolLoadBaseline.class.st +++ b/src/MCP/MCPToolLoadBaseline.class.st @@ -60,6 +60,12 @@ MCPToolLoadBaseline >> parsedRequestFromToolRequest: request [ ^ MCPLoadBaselineRequest fromRequest: request tool: self ] +{ #category : 'metadata' } +MCPToolLoadBaseline >> risk [ + + ^ 'medium' +] + { #category : 'metadata' } MCPToolLoadBaseline >> title [ diff --git a/src/MCP/MCPToolLoadMetacello.class.st b/src/MCP/MCPToolLoadMetacello.class.st index f2569f9..7eda329 100644 --- a/src/MCP/MCPToolLoadMetacello.class.st +++ b/src/MCP/MCPToolLoadMetacello.class.st @@ -15,6 +15,12 @@ MCPToolLoadMetacello class >> isAbstract [ ^ self = MCPToolLoadMetacello ] +{ #category : 'metadata' } +MCPToolLoadMetacello >> additionalKeywords [ + + ^ #( 'baseline' 'metacello' 'load' 'package' ) +] + { #category : 'private - execution' } MCPToolLoadMetacello >> commandForRequest: loadRequest [ @@ -119,6 +125,12 @@ MCPToolLoadMetacello >> failureSummaryForError: anError loadRequest: loadRequest nextPutAll: message ] ] +{ #category : 'metadata' } +MCPToolLoadMetacello >> groupName [ + + ^ 'repositories' +] + { #category : 'private - schema' } MCPToolLoadMetacello >> groupsSchemaProperty [ diff --git a/src/MCP/MCPToolLoadRepository.class.st b/src/MCP/MCPToolLoadRepository.class.st index 11f081e..0c3b329 100644 --- a/src/MCP/MCPToolLoadRepository.class.st +++ b/src/MCP/MCPToolLoadRepository.class.st @@ -225,6 +225,12 @@ MCPToolLoadRepository >> repositoryUrlSchemaProperty [ yourself ] +{ #category : 'metadata' } +MCPToolLoadRepository >> risk [ + + ^ 'medium' +] + { #category : 'testing' } MCPToolLoadRepository >> shouldSaveImageAfterSuccessfulExecution [ diff --git a/src/MCP/MCPToolMethodLookupOperation.class.st b/src/MCP/MCPToolMethodLookupOperation.class.st index 1af64ae..dbec36f 100644 --- a/src/MCP/MCPToolMethodLookupOperation.class.st +++ b/src/MCP/MCPToolMethodLookupOperation.class.st @@ -39,6 +39,12 @@ MCPToolMethodLookupOperation >> filterPatternFromRequest: request [ :value | value trimBoth ] ] +{ #category : 'metadata' } +MCPToolMethodLookupOperation >> groupName [ + + ^ 'methods' +] + { #category : 'private - schema' } MCPToolMethodLookupOperation >> lookupArgumentDescription [ diff --git a/src/MCP/MCPToolMethodMutation.class.st b/src/MCP/MCPToolMethodMutation.class.st index 3cc6bee..372cea0 100644 --- a/src/MCP/MCPToolMethodMutation.class.st +++ b/src/MCP/MCPToolMethodMutation.class.st @@ -31,6 +31,12 @@ MCPToolMethodMutation >> addParameterRefactoringForSelector: oldSelectorSymbol i newArgs: newArguments ] +{ #category : 'metadata' } +MCPToolMethodMutation >> additionalKeywords [ + + ^ #( 'method' 'refactor' 'refactoring' ) +] + { #category : 'private' } MCPToolMethodMutation >> allowedNonErrorCritiqueRuleClasses [ @@ -220,6 +226,12 @@ MCPToolMethodMutation >> failureMessageForRenameClassNamed: className classSide: (self normalizedCauseMessageForMutationError: anError) ] ] +{ #category : 'metadata' } +MCPToolMethodMutation >> groupName [ + + ^ 'methods' +] + { #category : 'private - schema' } MCPToolMethodMutation >> inputProperties [ diff --git a/src/MCP/MCPToolMethodSearch.class.st b/src/MCP/MCPToolMethodSearch.class.st index 0451b37..785c232 100644 --- a/src/MCP/MCPToolMethodSearch.class.st +++ b/src/MCP/MCPToolMethodSearch.class.st @@ -18,6 +18,12 @@ MCPToolMethodSearch class >> isAbstract [ ^ self = MCPToolMethodSearch ] +{ #category : 'metadata' } +MCPToolMethodSearch >> additionalKeywords [ + + ^ #( 'method' ) +] + { #category : 'metadata' } MCPToolMethodSearch >> buildOutputSchema [ @@ -85,6 +91,12 @@ MCPToolMethodSearch >> filterTextsForMethodEntry: anEntry sourceText: sourceText ^ #( ) ] +{ #category : 'metadata' } +MCPToolMethodSearch >> groupName [ + + ^ 'methods' +] + { #category : 'private - methods' } MCPToolMethodSearch >> implementorEntriesForQueryRequest: queryRequest [ diff --git a/src/MCP/MCPToolRegistration.class.st b/src/MCP/MCPToolRegistration.class.st index 1d03b02..e87090d 100644 --- a/src/MCP/MCPToolRegistration.class.st +++ b/src/MCP/MCPToolRegistration.class.st @@ -1,15 +1,11 @@ " -I describe one registered MCP tool. - -The registry owns registrations so a tool can be instantiated once and reused. The registration carries catalog metadata that belongs to registration time, such as the public group and default exposure. Server-specific exposure policy remains outside the tool and registration. +Registry entry wrapping one reusable MCPTool instance. Group and default exposure are delegated to the tool so registrations do not duplicate intrinsic metadata. " Class { #name : 'MCPToolRegistration', #superclass : 'Object', #instVars : [ - 'tool', - 'groupName', - 'defaultExposure' + 'tool' ], #category : 'MCP-Tools', #package : 'MCP', @@ -17,40 +13,27 @@ Class { } { #category : 'instance creation' } -MCPToolRegistration class >> tool: aTool groupName: aGroupName defaultExposure: anExposure [ - - ^ self new - initializeTool: aTool - groupName: aGroupName - defaultExposure: anExposure -] - -{ #category : 'constants' } -MCPToolRegistration class >> validExposureNames [ +MCPToolRegistration class >> tool: aTool [ - ^ #( 'static' 'discoverable' ) + ^ self new initializeTool: aTool ] { #category : 'accessing' } MCPToolRegistration >> defaultExposure [ - ^ defaultExposure + ^ self tool defaultExposure ] { #category : 'accessing' } MCPToolRegistration >> groupName [ - ^ groupName + ^ self tool groupName ] { #category : 'initialization' } -MCPToolRegistration >> initializeTool: aTool groupName: aGroupName defaultExposure: anExposure [ +MCPToolRegistration >> initializeTool: aTool [ - tool := aTool. - groupName := aGroupName asString. - defaultExposure := anExposure asString. - (self class validExposureNames includes: defaultExposure) ifFalse: [ - Error signal: 'Unknown MCP tool exposure: ' , defaultExposure ] + tool := aTool ] { #category : 'testing' } diff --git a/src/MCP/MCPToolRegistry.class.st b/src/MCP/MCPToolRegistry.class.st index 86a42ee..e498555 100644 --- a/src/MCP/MCPToolRegistry.class.st +++ b/src/MCP/MCPToolRegistry.class.st @@ -1,12 +1,12 @@ " -I hold the public MCP tool registry. +Instance-scoped catalog of MCP tools. -Each registration owns one reusable tool instance, its public group, and its default exposure. MCP server instances use an exposure policy to decide which registered tools are advertised directly through tools/list; discoverable tools remain callable through tool_call. +A registry discovers built-in concrete MCPTool subclasses, owns one reusable instance of each registered tool, and provides lookup, grouping, static-surface selection, and catalog search. Intrinsic metadata belongs to the tools. Visibility overrides belong to MCPToolExposurePolicy. " Class { #name : 'MCPToolRegistry', #superclass : 'Object', - #classInstVars : [ + #instVars : [ 'registrations' ], #category : 'MCP-Tools', @@ -14,473 +14,66 @@ Class { #tag : 'Tools' } -{ #category : 'tools' } -MCPToolRegistry class >> activationRequirementsForTool: aTool [ - - ^ #( ) -] - -{ #category : 'tools' } -MCPToolRegistry class >> additionalKeywordsForTool: aTool [ - - | toolName | - toolName := aTool name. - toolName = 'tool_get' ifTrue: [ - ^ #( 'tool' 'catalog' 'contract' 'schema' 'metadata' ) ]. - toolName = 'repository_search' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'branch' 'commit' 'remote' 'head' ) ]. - toolName = 'repository_identity_verify' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'guard' 'identity' ) ]. - toolName = 'repository_change_list' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'diff' 'status' 'change' ) ]. - toolName = 'repository_create' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'create' 'package' ) ]. - toolName = 'repository_attach' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'attach' 'package' ) ]. - toolName = 'repository_update' ifTrue: [ - ^ #( 'repo' 'iceberg' 'update' 'package' 'metadata' ) ]. - toolName = 'repository_export' ifTrue: [ - ^ #( 'repo' 'iceberg' 'tonel' 'export' 'write' ) ]. - toolName = 'repository_commit' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'commit' ) ]. - toolName = 'repository_fetch' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'remote' 'fetch' ) ]. - toolName = 'repository_pull' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'remote' 'pull' ) ]. - toolName = 'repository_push' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'remote' 'push' ) ]. - toolName = 'repository_branch_create' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'branch' 'create' ) ]. - toolName = 'repository_branch_switch' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'branch' 'switch' ) ]. - toolName = 'repository_branch_checkout' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'branch' 'checkout' 'reload' 'stale' - 'verification' ) ]. - toolName = 'repository_head_adopt' ifTrue: [ - ^ #( 'repo' 'iceberg' 'git' 'head' 'adopt' ) ]. - toolName = 'baseline_load' ifTrue: [ - ^ #( 'baseline' 'metacello' 'load' 'package' ) ]. - toolName = 'repository_load' ifTrue: [ - ^ #( 'repo' 'baseline' 'metacello' 'load' 'package' ) ]. - toolName = 'package_search' ifTrue: [ - ^ #( 'package' 'project' 'tag' ) ]. - toolName = 'class_search' ifTrue: [ - ^ #( 'class' 'package' 'tag' 'slot' 'trait' 'hierarchy' ) ]. - toolName = 'class_get' ifTrue: [ - ^ #( 'class' 'inspect' 'superclass' 'subclass' 'slot' 'trait' - 'comment' ) ]. - toolName = 'class_create' ifTrue: [ - ^ #( 'class' 'create' 'package' 'superclass' 'slot' 'trait' ) ]. - toolName = 'class_name_update' ifTrue: [ - ^ #( 'class' 'name' 'rename' 'update' 'refactor' 'refactoring' ) ]. - toolName = 'class_superclass_update' ifTrue: [ - ^ #( 'class' 'superclass' 'parent' 'update' 'reparent' 'refactor' - 'refactoring' ) ]. - toolName = 'class_package_update' ifTrue: [ - ^ #( 'class' 'package' 'tag' 'category' 'move' 'update' 'refactor' - 'refactoring' ) ]. - toolName = 'class_comment_update' ifTrue: [ - ^ #( 'class' 'comment' 'update' ) ]. - toolName = 'class_slots_update' ifTrue: [ - ^ #( 'class' 'slot' 'definition' 'update' ) ]. - toolName = 'class_traits_update' ifTrue: [ - ^ #( 'class' 'trait' 'composition' 'definition' 'update' ) ]. - toolName = 'class_side_traits_update' ifTrue: [ - ^ #( 'class' 'trait' 'side' 'metaclass' 'composition' 'definition' - 'update' ) ]. - toolName = 'class_shared_variables_update' ifTrue: [ - ^ #( 'class' 'variable' 'shared' 'classvar' 'binding' 'update' ) ]. - toolName = 'class_shared_pools_update' ifTrue: [ - ^ #( 'class' 'pool' 'shared' 'pooldictionary' 'dictionary' - 'update' ) ]. - toolName = 'class_layout_update' ifTrue: [ - ^ #( 'class' 'layout' 'shape' 'format' 'fixed' 'variable' - 'weak' 'immediate' 'word' 'byte' 'update' ) ]. - toolName = 'class_slot_add' ifTrue: [ - ^ #( 'class' 'slot' 'add' 'refactor' 'refactoring' ) ]. - toolName = 'class_slot_remove' ifTrue: [ - ^ #( 'class' 'slot' 'remove' 'refactor' 'refactoring' ) ]. - toolName = 'class_slot_name_update' ifTrue: [ - ^ #( 'class' 'slot' 'name' 'rename' 'update' 'refactor' 'refactoring' ) ]. - toolName = 'class_slot_pull_up' ifTrue: [ - ^ #( 'class' 'slot' 'pull' 'up' 'refactor' 'refactoring' ) ]. - toolName = 'class_slot_push_down' ifTrue: [ - ^ #( 'class' 'slot' 'push' 'down' 'refactor' 'refactoring' ) ]. - toolName = 'class_remove' ifTrue: [ - ^ #( 'class' 'remove' 'refactor' 'refactoring' ) ]. - toolName = 'method_metadata_search' ifTrue: [ - ^ #( 'method' 'selector' 'protocol' 'metadata' 'search' ) ]. - toolName = 'method_source_search' ifTrue: [ - ^ #( 'method' 'source' 'code' 'text' 'search' ) ]. - toolName = 'method_equivalent_search' ifTrue: [ - ^ #( 'method' 'equivalent' 'ast' 'duplicate' 'semantic' 'search' ) ]. - toolName = 'method_implementor_search' ifTrue: [ - ^ #( 'method' 'selector' 'implementor' 'implementation' ) ]. - toolName = 'method_sender_search' ifTrue: [ - ^ #( 'method' 'selector' 'sender' 'send' 'reference' ) ]. - toolName = 'method_class_reference_search' ifTrue: [ - ^ #( 'method' 'class' 'reference' 'binding' ) ]. - toolName = 'method_variable_reference_search' ifTrue: [ - ^ #( 'method' 'variable' 'reference' 'slot' 'temporary' 'classvar' - 'binding' ) ]. - toolName = 'method_get' ifTrue: [ - ^ #( 'method' 'selector' 'protocol' 'source' 'reference' 'variable' ) ]. - toolName = 'method_compile' ifTrue: [ - ^ #( 'method' 'source' 'protocol' 'create' 'compile' ) ]. - toolName = 'method_selector_update' ifTrue: [ - ^ #( 'method' 'selector' 'signature' 'rename' 'update' 'refactor' - 'refactoring' ) ]. - toolName = 'method_protocol_update' ifTrue: [ - ^ #( 'method' 'protocol' 'category' 'update' ) ]. - toolName = 'method_rewrite' ifTrue: [ - ^ #( 'method' 'rewrite' 'preview' 'apply' 'ast' 'pattern' 'refactor' - 'refactoring' ) ]. - toolName = 'test_run' ifTrue: [ ^ #( 'test' 'sunit' 'timeout' ) ]. - toolName = 'test_coverage_run' ifTrue: [ - ^ #( 'test' 'sunit' 'coverage' 'timeout' ) ]. - toolName = 'history_file_list' ifTrue: [ - ^ #( 'history' 'change' 'file' 'list' 'ombu' 'epicea' ) ]. - toolName = 'history_entry_list' ifTrue: [ - ^ #( 'history' 'change' 'entry' 'browse' 'list' 'inspect' 'ombu' - 'epicea' ) ]. - toolName = 'history_entry_apply' ifTrue: [ - ^ #( 'history' 'change' 'entry' 'preview' 'apply' 'recover' - 'epicea' 'ombu' ) ]. - toolName = 'history_entry_revert' ifTrue: [ - ^ #( 'history' 'change' 'entry' 'preview' 'revert' 'recover' - 'epicea' 'ombu' ) ]. - toolName = 'screenshot_capture' ifTrue: [ - ^ #( 'screenshot' 'capture' 'window' 'display' 'world' 'ui' ) ]. - toolName = 'image_evaluate' ifTrue: [ - ^ #( 'expression' 'smalltalk' 'code' ) ]. - (toolName beginsWith: 'debug_') ifTrue: [ - ^ #( 'debug' 'session' 'state' 'frame' 'variable' 'breakpoint' - 'capture' 'control' 'repair' 'exception' ) ]. - ^ #( ) -] - -{ #category : 'tools' } -MCPToolRegistry class >> contractForTool: aTool [ - - ^ self - contractForTool: aTool - usingPolicy: MCPToolExposurePolicy default -] - -{ #category : 'tools' } -MCPToolRegistry class >> contractForTool: aTool usingPolicy: aToolExposurePolicy [ - - ^ self - contractForTool: aTool - usingPolicy: aToolExposurePolicy - includeOutputSchema: true -] - -{ #category : 'tools' } -MCPToolRegistry class >> contractForTool: aTool usingPolicy: aToolExposurePolicy includeOutputSchema: includeOutputSchema [ - - | activationRequirements data examples | - data := (self metadataForTool: aTool usingPolicy: aToolExposurePolicy) - copy. - data at: #inputSchema put: aTool inputSchema asJRPCJSON. - includeOutputSchema ifTrue: [ - data at: #outputSchema put: aTool outputSchema asJRPCJSON ]. - examples := self examplesForTool: aTool. - examples ifNotEmpty: [ data at: #examples put: examples ]. - activationRequirements := self activationRequirementsForTool: aTool. - activationRequirements ifNotEmpty: [ - data at: #activationRequirements put: activationRequirements ]. - ^ data -] - { #category : 'registrations' } -MCPToolRegistry class >> defaultExposureForToolClass: aToolClass [ - - ^ (self staticToolClasses includes: aToolClass) - ifTrue: [ 'static' ] - ifFalse: [ 'discoverable' ] -] +MCPToolRegistry >> defaultRegistrations [ -{ #category : 'tools' } -MCPToolRegistry class >> defaultPublicToolGroups [ - - ^ { - ('tool-catalog' -> { - MCPToolSearchTools. - MCPToolGetTool. - MCPToolCallTool }). - ('repositories' -> { - MCPToolSearchRepositories. - MCPToolVerifyRepositoryIdentity. - MCPToolListRepositoryChanges. - MCPToolDiscardRepositoryChanges. - MCPToolCreateRepository. - MCPToolAttachRepository. - MCPToolUpdateRepository. - MCPToolAddRepositoryRemote. - MCPToolRemoveRepositoryRemote. - MCPToolUpdateRepositoryRemote. - - MCPToolExportRepository. - MCPToolCommitRepository. - MCPToolFetchRepository. - MCPToolPullRepository. - MCPToolPushRepository. - MCPToolCreateRepositoryBranch. - MCPToolSwitchRepositoryBranch. - MCPToolCheckoutRepositoryBranch. - MCPToolAdoptRepositoryHead. - MCPToolLoadBaseline. - MCPToolLoadRepository }). - ('packages' -> { MCPToolSearchPackages }). - ('classes' -> { - MCPToolSearchClasses. - MCPToolGetClass. - MCPToolCreateClass. - MCPToolUpdateClassName. - MCPToolUpdateClassSuperclass. - MCPToolUpdateClassPackage. - MCPToolUpdateClassComment. - MCPToolUpdateClassSlots. - MCPToolUpdateClassTraits. - MCPToolUpdateClassSideTraits. - MCPToolUpdateClassSharedVariables. - MCPToolUpdateClassSharedPools. - MCPToolUpdateClassLayout. - MCPToolAddClassSlot. - MCPToolRemoveClassSlot. - MCPToolUpdateClassSlotName. - MCPToolPullUpClassSlot. - MCPToolPushDownClassSlot. - MCPToolRemoveClasses }). - ('methods' -> { - MCPToolSearchMethodMetadata. - MCPToolSearchMethodSource. - MCPToolSearchEquivalentMethods. - MCPToolSearchMethodImplementors. - MCPToolSearchMethodSenders. - MCPToolSearchClassReferences. - MCPToolSearchVariableReferences. - MCPToolGetMethod. - MCPToolCompileMethod. - MCPToolUpdateMethodSelector. - MCPToolUpdateMethodProtocol. - MCPToolRewriteMethods. - MCPToolRemoveMethods }). - ('history' -> { - MCPToolListChangeHistoryFiles. - MCPToolListChangeHistoryEntries. - MCPToolApplyChangeHistoryEntries. - MCPToolRevertChangeHistoryEntries }). - ('tests' -> { - MCPToolRunTests. - MCPToolRunTestCoverage }). - ('ui' -> { MCPToolCaptureScreenshot }). - ('debugging' -> { - MCPToolListDebugSessions. - MCPToolGetDebugSession. - MCPToolRemoveDebugSession. - MCPToolSearchDebugCandidates. - MCPToolAttachDebugCandidate. - MCPToolDebugState. - MCPToolDebugVariables. - MCPToolDebugEvaluate. - MCPToolStepIntoDebugSession. - MCPToolStepOverDebugSession. - MCPToolStepThroughDebugSession. - MCPToolRestartDebugSession. - MCPToolResumeDebugSession. - MCPToolTerminateDebugSession. - MCPToolListDebugBreakpoints. - MCPToolSetDebugBreakpoint. - MCPToolRemoveDebugBreakpoint. - MCPToolEnableDebugBreakpoint. - MCPToolDisableDebugBreakpoint. - MCPToolClearDebugBreakpoints. - MCPToolDebugCapture. - MCPToolDebugTest. - MCPToolUpdateDebugMethod }). - ('runtime' -> { MCPToolEvaluate }) } -] - -{ #category : 'registrations' } -MCPToolRegistry class >> defaultRegistrations [ - - ^ Array streamContents: [ :stream | - self defaultPublicToolGroups do: [ :group | - group value do: [ :toolClass | - stream nextPut: (MCPToolRegistration - tool: toolClass new - groupName: group key - defaultExposure: - (self defaultExposureForToolClass: toolClass)) ] ] ] -] - -{ #category : 'tools' } -MCPToolRegistry class >> defaultStaticToolNames [ - - ^ (self registrations - select: [ :each | each isStaticByDefault ] - thenCollect: [ :each | each name ]) asArray -] - -{ #category : 'tools' } -MCPToolRegistry class >> examplesForTool: aTool [ - - ^ #( ) -] - -{ #category : 'tools' } -MCPToolRegistry class >> exposureForTool: aTool [ - - ^ self - exposureForTool: aTool - usingPolicy: MCPToolExposurePolicy default -] - -{ #category : 'tools' } -MCPToolRegistry class >> exposureForTool: aTool usingPolicy: aToolExposurePolicy [ - - ^ aToolExposurePolicy exposureForToolRegistration: - (self registrationForTool: aTool ifAbsent: [ ^ 'discoverable' ]) -] - -{ #category : 'tools' } -MCPToolRegistry class >> groupNameForToolClass: aToolClass [ - - ^ (self registrationForToolClass: aToolClass ifAbsent: [ ^ 'other' ]) - groupName + | tools | + tools := (MCPTool concreteSubclasses select: [ :toolClass | toolClass isBuiltIn ] thenCollect: [ :toolClass | toolClass new ]) + asArray. + tools sort: [ :left :right | + left groupName = right groupName + ifTrue: [ left name <= right name ] + ifFalse: [ left groupName <= right groupName ] ]. + ^ tools collect: [ :tool | MCPToolRegistration tool: tool ] ] { #category : 'testing' } -MCPToolRegistry class >> includesToolNamed: aToolName [ +MCPToolRegistry >> includesToolNamed: aToolName [ ^ self publicToolNames includes: aToolName asString ] { #category : 'initialization' } -MCPToolRegistry class >> initialize [ +MCPToolRegistry >> initialize [ + super initialize. self resetRegistrations ] -{ #category : 'testing' } -MCPToolRegistry class >> isStaticTool: aToolName [ - - ^ self - isStaticTool: aToolName - usingPolicy: MCPToolExposurePolicy default -] - -{ #category : 'testing' } -MCPToolRegistry class >> isStaticTool: aToolName usingPolicy: aToolExposurePolicy [ - - ^ aToolExposurePolicy isStaticTool: aToolName -] - -{ #category : 'tools' } -MCPToolRegistry class >> keywordsForTool: aTool [ - - | keywords normalizedKeywords | - keywords := OrderedCollection new. - keywords addAll: (aTool name findTokens: '-_'). - keywords addAll: (aTool title findTokens: ' -'). - keywords addAll: - ((self groupNameForToolClass: aTool class) findTokens: '-_'). - keywords addAll: (self additionalKeywordsForTool: aTool). - normalizedKeywords := OrderedCollection new. - keywords do: [ :each | - normalizedKeywords addAll: (each asString findTokens: '-_ ') ]. - ^ (normalizedKeywords collect: [ :each | each asLowercase ]) asSet - asArray -] - -{ #category : 'tools' } -MCPToolRegistry class >> metadataForTool: aTool [ - - ^ self - metadataForTool: aTool - usingPolicy: MCPToolExposurePolicy default -] - -{ #category : 'tools' } -MCPToolRegistry class >> metadataForTool: aTool usingPolicy: aToolExposurePolicy [ - - ^ { - (#name -> aTool name). - (#description -> aTool description) } asDictionary -] - -{ #category : 'tools' } -MCPToolRegistry class >> mutabilityForTool: aTool [ - - | executeTools readTools toolName | - toolName := aTool name. - readTools := #( 'tool_search' 'tool_get' 'repository_search' - 'repository_identity_verify' - 'repository_change_list' 'package_search' - 'class_search' 'class_get' 'method_metadata_search' - 'method_source_search' 'method_equivalent_search' - 'method_implementor_search' - 'method_sender_search' 'method_class_reference_search' - 'method_variable_reference_search' - 'method_get' 'history_file_list' 'history_entry_list' - 'screenshot_capture' 'debug_session_list' - 'debug_session_get' 'debug_candidate_search' - 'debug_state_get' 'debug_variable_get' - 'debug_breakpoint_list' ). - executeTools := #( 'tool_call' 'test_run' 'test_coverage_run' - 'image_evaluate' 'debug_expression_evaluate' - 'debug_capture' 'debug_test_run' ). - (readTools includes: toolName) ifTrue: [ ^ 'read' ]. - (executeTools includes: toolName) ifTrue: [ ^ 'execute' ]. - ^ 'write' -] - { #category : 'tools' } -MCPToolRegistry class >> publicToolClasses [ +MCPToolRegistry >> publicToolClasses [ ^ (self registrations collect: [ :each | each toolClass ]) asArray ] { #category : 'tools' } -MCPToolRegistry class >> publicToolGroups [ +MCPToolRegistry >> publicToolGroups [ | groups | groups := OrderedCollection new. self registrations do: [ :registration | | group | - group := groups - detect: [ :each | each key = registration groupName ] - ifNone: [ - groups add: - registration groupName -> OrderedCollection new. - groups last ]. + group := groups detect: [ :each | each key = registration groupName ] ifNone: [ + groups add: registration groupName -> OrderedCollection new. + groups last ]. group value add: registration toolClass ]. - ^ (groups collect: [ :each | each key -> each value asArray ]) - asArray + ^ (groups collect: [ :each | each key -> each value asArray ]) asArray ] { #category : 'tools' } -MCPToolRegistry class >> publicToolNames [ +MCPToolRegistry >> publicToolNames [ ^ (self registrations collect: [ :each | each name ]) asArray ] { #category : 'tools' } -MCPToolRegistry class >> publicTools [ +MCPToolRegistry >> publicTools [ ^ self registrations collect: [ :each | each tool ] ] { #category : 'tools' } -MCPToolRegistry class >> publicToolsUsingPolicy: aToolExposurePolicy [ - - ^ self publicTools -] - -{ #category : 'tools' } -MCPToolRegistry class >> queryTermAlternativesFor: aTerm [ +MCPToolRegistry >> queryTermAlternativesFor: aTerm [ | alternatives singular term | term := aTerm asString asLowercase. @@ -491,265 +84,136 @@ MCPToolRegistry class >> queryTermAlternativesFor: aTerm [ ] { #category : 'tools' } -MCPToolRegistry class >> queryTermsFrom: queryString [ +MCPToolRegistry >> queryTermsFrom: queryString [ | normalized | - normalized := queryString - ifNil: [ ^ #( ) ] - ifNotNil: [ :value | - value asString trimBoth asLowercase ]. + normalized := queryString ifNil: [ ^ #( ) ] ifNotNil: [ :value | value asString trimBoth asLowercase ]. normalized isEmpty ifTrue: [ ^ #( ) ]. ^ normalized findTokens: ' -_' ] { #category : 'registrations' } -MCPToolRegistry class >> registerTool: aTool groupName: aGroupName defaultExposure: anExposure [ +MCPToolRegistry >> registerTool: aTool [ | registration | - registration := MCPToolRegistration - tool: aTool - groupName: aGroupName - defaultExposure: anExposure. - (self includesToolNamed: registration name) ifTrue: [ - Error signal: 'MCP tool already registered: ' , registration name ]. + registration := MCPToolRegistration tool: aTool. + (self includesToolNamed: registration name) ifTrue: [ Error signal: 'MCP tool already registered: ' , registration name ]. registrations := self registrations copyWith: registration. ^ registration ] { #category : 'registrations' } -MCPToolRegistry class >> registerToolClass: aToolClass groupName: aGroupName defaultExposure: anExposure [ +MCPToolRegistry >> registerToolClass: aToolClass [ - ^ self - registerTool: aToolClass new - groupName: aGroupName - defaultExposure: anExposure + ^ self registerTool: aToolClass new ] { #category : 'registrations' } -MCPToolRegistry class >> registrationForTool: aTool ifAbsent: absentBlock [ +MCPToolRegistry >> registrationForTool: aTool ifAbsent: absentBlock [ ^ self registrationNamed: aTool name ifAbsent: absentBlock ] { #category : 'registrations' } -MCPToolRegistry class >> registrationForToolClass: aToolClass ifAbsent: absentBlock [ +MCPToolRegistry >> registrationForToolClass: aToolClass ifAbsent: absentBlock [ - ^ self registrations - detect: [ :each | each toolClass = aToolClass ] - ifNone: absentBlock + ^ self registrations detect: [ :each | each toolClass = aToolClass ] ifNone: absentBlock ] { #category : 'registrations' } -MCPToolRegistry class >> registrationNamed: aToolName ifAbsent: absentBlock [ +MCPToolRegistry >> registrationNamed: aToolName ifAbsent: absentBlock [ | toolName | toolName := aToolName asString. - ^ self registrations - detect: [ :each | each name = toolName ] - ifNone: absentBlock + ^ self registrations detect: [ :each | each name = toolName ] ifNone: absentBlock ] { #category : 'registrations' } -MCPToolRegistry class >> registrations [ +MCPToolRegistry >> registrations [ - ^ registrations ifNil: [ registrations := self defaultRegistrations ] + ^ registrations ] { #category : 'registrations' } -MCPToolRegistry class >> resetRegistrations [ - - registrations := nil -] - -{ #category : 'tools' } -MCPToolRegistry class >> riskForTool: aTool [ - - | lowRisk mediumRisk toolName | - toolName := aTool name. - lowRisk := #( 'tool_search' 'tool_get' 'repository_search' - 'repository_identity_verify' - 'repository_change_list' 'package_search' - 'class_search' 'class_get' 'method_metadata_search' - 'method_source_search' 'method_equivalent_search' - 'method_implementor_search' 'method_sender_search' - 'method_class_reference_search' - 'method_variable_reference_search' - 'method_get' 'history_file_list' 'history_entry_list' - 'screenshot_capture' 'debug_session_list' - 'debug_session_get' 'debug_candidate_search' - 'debug_state_get' 'debug_variable_get' - 'debug_breakpoint_list' ). - mediumRisk := #( 'tool_call' 'baseline_load' 'repository_load' - 'test_run' 'test_coverage_run' 'image_evaluate' - 'debug_expression_evaluate' - 'debug_capture' 'debug_test_run' ). - (lowRisk includes: toolName) ifTrue: [ ^ 'low' ]. - (mediumRisk includes: toolName) ifTrue: [ ^ 'medium' ]. - ^ 'high' -] - -{ #category : 'tools' } -MCPToolRegistry class >> searchMetadataForTool: aTool usingPolicy: aToolExposurePolicy [ +MCPToolRegistry >> resetRegistrations [ - ^ { - (#name -> aTool name). - (#description -> aTool description) } asDictionary + registrations := self defaultRegistrations ] { #category : 'tools' } -MCPToolRegistry class >> searchableTextForTool: aTool [ - - ^ String streamContents: [ :stream | - stream - nextPutAll: aTool name; - space; - nextPutAll: aTool title; - space; - nextPutAll: aTool description; - space; - nextPutAll: (self groupNameForToolClass: aTool class). - (self keywordsForTool: aTool) do: [ :keyword | - stream - space; - nextPutAll: keyword ] ] -] - -{ #category : 'tools' } -MCPToolRegistry class >> singularSearchTermFor: aTerm [ +MCPToolRegistry >> singularSearchTermFor: aTerm [ | term | term := aTerm asString asLowercase. - term = 'classes' ifTrue: [ ^ 'class' ]. - term = 'repositories' ifTrue: [ ^ 'repository' ]. - term = 'histories' ifTrue: [ ^ 'history' ]. (term endsWith: 'ies') ifTrue: [ ^ (term allButLast: 3) , 'y' ]. (term endsWith: 'ses') ifTrue: [ ^ term allButLast: 2 ]. - ((term endsWith: 's') and: [ (term endsWith: 'ss') not ]) ifTrue: [ - ^ term allButLast ]. + ((term endsWith: 's') and: [ (term endsWith: 'ss') not ]) ifTrue: [ ^ term allButLast ]. ^ term ] -{ #category : 'accessing' } -MCPToolRegistry class >> staticToolClasses [ - - ^ { - MCPToolSearchTools. - MCPToolGetTool. - MCPToolCallTool. - MCPToolSearchPackages. - MCPToolSearchClasses. - MCPToolGetClass. - MCPToolCreateClass. - MCPToolSearchMethodMetadata. - MCPToolSearchMethodSource. - MCPToolSearchMethodImplementors. - MCPToolSearchMethodSenders. - MCPToolGetMethod. - MCPToolCompileMethod. - MCPToolUpdateMethodSelector. - MCPToolEvaluate. - MCPToolRunTests } -] - { #category : 'tools' } -MCPToolRegistry class >> staticToolNames [ +MCPToolRegistry >> staticToolNames [ - ^ self defaultStaticToolNames + ^ (self registrations select: [ :each | each isStaticByDefault ] thenCollect: [ :each | each name ]) asArray ] { #category : 'tools' } -MCPToolRegistry class >> staticTools [ +MCPToolRegistry >> staticTools [ - ^ self staticToolsUsingPolicy: MCPToolExposurePolicy default + ^ self staticToolsUsingPolicy: (MCPToolExposurePolicy forRegistry: self) ] { #category : 'tools' } -MCPToolRegistry class >> staticToolsDictionary [ +MCPToolRegistry >> staticToolsDictionary [ - ^ self staticToolsDictionaryUsingPolicy: - MCPToolExposurePolicy default + ^ self staticToolsDictionaryUsingPolicy: (MCPToolExposurePolicy forRegistry: self) ] { #category : 'tools' } -MCPToolRegistry class >> staticToolsDictionaryUsingPolicy: aToolExposurePolicy [ +MCPToolRegistry >> staticToolsDictionaryUsingPolicy: aToolExposurePolicy [ | dictionary | dictionary := Dictionary new. - (self staticToolsUsingPolicy: aToolExposurePolicy) do: [ :tool | - dictionary at: tool name put: tool ]. + (self staticToolsUsingPolicy: aToolExposurePolicy) do: [ :tool | dictionary at: tool name put: tool ]. ^ dictionary ] { #category : 'tools' } -MCPToolRegistry class >> staticToolsUsingPolicy: aToolExposurePolicy [ +MCPToolRegistry >> staticToolsUsingPolicy: aToolExposurePolicy [ - ^ (self registrations - select: [ :each | - aToolExposurePolicy isStaticToolRegistration: each ] - thenCollect: [ :each | each tool ]) asArray -] - -{ #category : 'tools' } -MCPToolRegistry class >> tool: aTool matchesQuery: queryString group: groupString [ - - ^ self - tool: aTool - matchesQuery: queryString - group: groupString - usingPolicy: MCPToolExposurePolicy default + ^ (self registrations select: [ :each | aToolExposurePolicy isStaticToolRegistration: each ] thenCollect: [ :each | each tool ]) + asArray ] { #category : 'tools' } -MCPToolRegistry class >> tool: aTool matchesQuery: queryString group: groupString usingPolicy: aToolExposurePolicy [ +MCPToolRegistry >> tool: aTool matchesQuery: queryString group: groupString [ | group queryTerms searchableText | - group := groupString ifNotNil: [ :value | - value asString trimBoth asLowercase ]. - (group isNotNil and: [ - group isNotEmpty and: [ - group ~= (self groupNameForToolClass: aTool class) ] ]) ifTrue: [ - ^ false ]. + group := groupString ifNotNil: [ :value | value asString trimBoth asLowercase ]. + (group isNotNil and: [ group isNotEmpty and: [ group ~= aTool groupName ] ]) ifTrue: [ ^ false ]. queryTerms := self queryTermsFrom: queryString. queryTerms ifEmpty: [ ^ true ]. - searchableText := (self searchableTextForTool: aTool) asLowercase. + searchableText := aTool searchableText asLowercase. ^ queryTerms anySatisfy: [ :each | - (self queryTermAlternativesFor: each) anySatisfy: [ :alternative | - searchableText includesSubstring: alternative ] ] + (self queryTermAlternativesFor: each) anySatisfy: [ :alternative | searchableText includesSubstring: alternative ] ] ] { #category : 'tools' } -MCPToolRegistry class >> toolNamed: aToolName ifAbsent: absentBlock [ +MCPToolRegistry >> toolNamed: aToolName ifAbsent: absentBlock [ - ^ self - toolNamed: aToolName - ifAbsent: absentBlock - usingPolicy: MCPToolExposurePolicy default + ^ (self registrationNamed: aToolName ifAbsent: [ ^ absentBlock value ]) tool ] { #category : 'tools' } -MCPToolRegistry class >> toolNamed: aToolName ifAbsent: absentBlock usingPolicy: aToolExposurePolicy [ +MCPToolRegistry >> toolsMatchingQuery: queryString group: groupString [ - ^ (self - registrationNamed: aToolName - ifAbsent: [ ^ absentBlock value ]) tool -] - -{ #category : 'tools' } -MCPToolRegistry class >> toolsMatchingQuery: queryString group: groupString usingPolicy: aToolExposurePolicy [ - - ^ (self publicTools select: [ :tool | - self - tool: tool - matchesQuery: queryString - group: groupString - usingPolicy: aToolExposurePolicy ]) asArray + ^ (self publicTools select: [ :tool | self tool: tool matchesQuery: queryString group: groupString ]) asArray ] { #category : 'registrations' } -MCPToolRegistry class >> unregisterToolNamed: aToolName [ +MCPToolRegistry >> unregisterToolNamed: aToolName [ | toolName | toolName := aToolName asString. - registrations := self registrations reject: [ :each | - each name = toolName ] + registrations := self registrations reject: [ :each | each name = toolName ] ] diff --git a/src/MCP/MCPToolRemoveClasses.class.st b/src/MCP/MCPToolRemoveClasses.class.st index 56583c2..d91cdfe 100644 --- a/src/MCP/MCPToolRemoveClasses.class.st +++ b/src/MCP/MCPToolRemoveClasses.class.st @@ -144,6 +144,12 @@ MCPToolRemoveClasses >> failureMessageForClassNames: classNames error: anError [ nextPutAll: anError messageText ] ] +{ #category : 'metadata' } +MCPToolRemoveClasses >> groupName [ + + ^ 'classes' +] + { #category : 'private - request' } MCPToolRemoveClasses >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolRemoveMethods.class.st b/src/MCP/MCPToolRemoveMethods.class.st index 10506e3..66cc17c 100644 --- a/src/MCP/MCPToolRemoveMethods.class.st +++ b/src/MCP/MCPToolRemoveMethods.class.st @@ -162,6 +162,12 @@ MCPToolRemoveMethods >> forceRemoveSelectors: selectorSymbols from: aBehavior [ refactoring performChanges ] +{ #category : 'metadata' } +MCPToolRemoveMethods >> groupName [ + + ^ 'methods' +] + { #category : 'private - request' } MCPToolRemoveMethods >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolRepositoryOperation.class.st b/src/MCP/MCPToolRepositoryOperation.class.st index ab1b9e9..a74007e 100644 --- a/src/MCP/MCPToolRepositoryOperation.class.st +++ b/src/MCP/MCPToolRepositoryOperation.class.st @@ -17,6 +17,12 @@ MCPToolRepositoryOperation class >> isAbstract [ ^ self = MCPToolRepositoryOperation ] +{ #category : 'metadata' } +MCPToolRepositoryOperation >> additionalKeywords [ + + ^ #( 'repository' 'repo' 'iceberg' 'git' ) +] + { #category : 'private - schema' } MCPToolRepositoryOperation >> branchNameSchemaProperty [ @@ -71,6 +77,12 @@ MCPToolRepositoryOperation >> errorResultForCommandError: anError action: action ^ self errorResultText: anError messageText details: details ] +{ #category : 'metadata' } +MCPToolRepositoryOperation >> groupName [ + + ^ 'repositories' +] + { #category : 'private - schema' } MCPToolRepositoryOperation >> inputProperties [ diff --git a/src/MCP/MCPToolRewriteMethods.class.st b/src/MCP/MCPToolRewriteMethods.class.st index 24efe39..fb7dc2c 100644 --- a/src/MCP/MCPToolRewriteMethods.class.st +++ b/src/MCP/MCPToolRewriteMethods.class.st @@ -75,6 +75,12 @@ MCPToolRewriteMethods >> executeWithRequest: request [ rawRequest: request ] ] +{ #category : 'metadata' } +MCPToolRewriteMethods >> groupName [ + + ^ 'methods' +] + { #category : 'private - execution' } MCPToolRewriteMethods >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolRunTestCoverage.class.st b/src/MCP/MCPToolRunTestCoverage.class.st index 5bd3bc5..08f4be0 100644 --- a/src/MCP/MCPToolRunTestCoverage.class.st +++ b/src/MCP/MCPToolRunTestCoverage.class.st @@ -43,6 +43,12 @@ MCPToolRunTestCoverage >> buildOutputSchema [ required: #( 'runCount' 'passedCount' 'coverage' ) ] +{ #category : 'metadata' } +MCPToolRunTestCoverage >> defaultExposure [ + + ^ 'discoverable' +] + { #category : 'metadata' } MCPToolRunTestCoverage >> description [ diff --git a/src/MCP/MCPToolRunTests.class.st b/src/MCP/MCPToolRunTests.class.st index 4da250a..c91e73b 100644 --- a/src/MCP/MCPToolRunTests.class.st +++ b/src/MCP/MCPToolRunTests.class.st @@ -19,6 +19,12 @@ MCPToolRunTests class >> toolName [ ^ 'test_run' ] +{ #category : 'metadata' } +MCPToolRunTests >> additionalKeywords [ + + ^ #( 'test' 'sunit' 'timeout' ) +] + { #category : 'metadata' } MCPToolRunTests >> buildInputSchema [ @@ -214,6 +220,12 @@ MCPToolRunTests >> defaultCoverageMethodLimit [ ^ 50 ] +{ #category : 'metadata' } +MCPToolRunTests >> defaultExposure [ + + ^ 'static' +] + { #category : 'defaults' } MCPToolRunTests >> defaultTimeoutSeconds [ @@ -277,6 +289,18 @@ MCPToolRunTests >> failureMessageForTestNames: testNames error: anError [ nextPutAll: anError messageText ] ] +{ #category : 'metadata' } +MCPToolRunTests >> groupName [ + + ^ 'tests' +] + +{ #category : 'metadata' } +MCPToolRunTests >> mutability [ + + ^ 'execute' +] + { #category : 'private - request' } MCPToolRunTests >> parsedRequestFromToolRequest: request [ @@ -302,6 +326,12 @@ MCPToolRunTests >> rawTestNamesFromRequest: rawRequest [ fromRequest: rawRequest) do: [ :each | stream nextPut: each ] ] ] ] +{ #category : 'metadata' } +MCPToolRunTests >> risk [ + + ^ 'medium' +] + { #category : 'private' } MCPToolRunTests >> successSummaryForData: data [ diff --git a/src/MCP/MCPToolSearch.class.st b/src/MCP/MCPToolSearch.class.st index 5a936a3..2ea30e3 100644 --- a/src/MCP/MCPToolSearch.class.st +++ b/src/MCP/MCPToolSearch.class.st @@ -349,6 +349,12 @@ MCPToolSearch >> minimalPagedQueryOutputSchemaForEntriesPropertyNamed: entriesPr required: { entriesPropertyName } ] +{ #category : 'metadata' } +MCPToolSearch >> mutability [ + + ^ 'read' +] + { #category : 'private - filtering' } MCPToolSearch >> normalizeFilterString: aValue caseSensitive: aBoolean [ @@ -516,6 +522,12 @@ MCPToolSearch >> requestedContextFromRequest: request [ ^ self subclassResponsibility ] +{ #category : 'metadata' } +MCPToolSearch >> risk [ + + ^ 'low' +] + { #category : 'private - results' } MCPToolSearch >> scopeQueryFailureMessageFor: resultKind scope: scopeSummary error: anError [ diff --git a/src/MCP/MCPToolSearchClasses.class.st b/src/MCP/MCPToolSearchClasses.class.st index 0cd02de..51528e7 100644 --- a/src/MCP/MCPToolSearchClasses.class.st +++ b/src/MCP/MCPToolSearchClasses.class.st @@ -122,6 +122,12 @@ MCPToolSearchClasses >> classInfoDataProperties [ description: 'Package tag.') } ] +{ #category : 'metadata' } +MCPToolSearchClasses >> defaultExposure [ + + ^ 'static' +] + { #category : 'metadata' } MCPToolSearchClasses >> description [ @@ -148,6 +154,12 @@ MCPToolSearchClasses >> fieldTextsForClassEntry: anEntry fieldName: fieldName [ ^ #( ) ] +{ #category : 'metadata' } +MCPToolSearchClasses >> groupName [ + + ^ 'classes' +] + { #category : 'private - filtering' } MCPToolSearchClasses >> matchesFieldFiltersOnClassEntry: anEntry queryRequest: queryRequest [ diff --git a/src/MCP/MCPToolSearchDebugCandidates.class.st b/src/MCP/MCPToolSearchDebugCandidates.class.st index 03ae38e..10d37aa 100644 --- a/src/MCP/MCPToolSearchDebugCandidates.class.st +++ b/src/MCP/MCPToolSearchDebugCandidates.class.st @@ -15,6 +15,18 @@ MCPToolSearchDebugCandidates class >> toolName [ ^ 'debug_candidate_search' ] +{ #category : 'metadata' } +MCPToolSearchDebugCandidates >> mutability [ + + ^ 'read' +] + +{ #category : 'metadata' } +MCPToolSearchDebugCandidates >> risk [ + + ^ 'low' +] + { #category : 'private - specs' } MCPToolSearchDebugCandidates >> sessionToolSpec [ diff --git a/src/MCP/MCPToolSearchMethodImplementors.class.st b/src/MCP/MCPToolSearchMethodImplementors.class.st index a82449b..9306a3c 100644 --- a/src/MCP/MCPToolSearchMethodImplementors.class.st +++ b/src/MCP/MCPToolSearchMethodImplementors.class.st @@ -15,6 +15,12 @@ MCPToolSearchMethodImplementors class >> toolName [ ^ 'method_implementor_search' ] +{ #category : 'metadata' } +MCPToolSearchMethodImplementors >> defaultExposure [ + + ^ 'static' +] + { #category : 'metadata' } MCPToolSearchMethodImplementors >> description [ diff --git a/src/MCP/MCPToolSearchMethodMetadata.class.st b/src/MCP/MCPToolSearchMethodMetadata.class.st index e10ebb7..db1d57f 100644 --- a/src/MCP/MCPToolSearchMethodMetadata.class.st +++ b/src/MCP/MCPToolSearchMethodMetadata.class.st @@ -36,6 +36,12 @@ MCPToolSearchMethodMetadata >> buildInputSchema [ ^ self queryInputSchemaWithProperties: properties ] +{ #category : 'metadata' } +MCPToolSearchMethodMetadata >> defaultExposure [ + + ^ 'static' +] + { #category : 'metadata' } MCPToolSearchMethodMetadata >> description [ diff --git a/src/MCP/MCPToolSearchMethodSenders.class.st b/src/MCP/MCPToolSearchMethodSenders.class.st index 031fc0d..1cfcf8c 100644 --- a/src/MCP/MCPToolSearchMethodSenders.class.st +++ b/src/MCP/MCPToolSearchMethodSenders.class.st @@ -15,6 +15,12 @@ MCPToolSearchMethodSenders class >> toolName [ ^ 'method_sender_search' ] +{ #category : 'metadata' } +MCPToolSearchMethodSenders >> defaultExposure [ + + ^ 'static' +] + { #category : 'metadata' } MCPToolSearchMethodSenders >> description [ diff --git a/src/MCP/MCPToolSearchMethodSource.class.st b/src/MCP/MCPToolSearchMethodSource.class.st index 2ef0677..0124c25 100644 --- a/src/MCP/MCPToolSearchMethodSource.class.st +++ b/src/MCP/MCPToolSearchMethodSource.class.st @@ -39,6 +39,12 @@ MCPToolSearchMethodSource >> buildInputSchema [ yourself ] +{ #category : 'metadata' } +MCPToolSearchMethodSource >> defaultExposure [ + + ^ 'static' +] + { #category : 'private - request' } MCPToolSearchMethodSource >> defaultFilterMode [ diff --git a/src/MCP/MCPToolSearchPackages.class.st b/src/MCP/MCPToolSearchPackages.class.st index ef34db9..c7681f5 100644 --- a/src/MCP/MCPToolSearchPackages.class.st +++ b/src/MCP/MCPToolSearchPackages.class.st @@ -43,6 +43,12 @@ MCPToolSearchPackages >> buildOutputSchema [ itemNounPhrase: 'package entries' ] +{ #category : 'metadata' } +MCPToolSearchPackages >> defaultExposure [ + + ^ 'static' +] + { #category : 'metadata' } MCPToolSearchPackages >> description [ @@ -67,6 +73,12 @@ MCPToolSearchPackages >> fieldTextsForPackageEntry: anEntry fieldName: fieldName ^ #( ) ] +{ #category : 'metadata' } +MCPToolSearchPackages >> groupName [ + + ^ 'packages' +] + { #category : 'private - filtering' } MCPToolSearchPackages >> matchesFieldFiltersOnPackageEntry: anEntry queryRequest: queryRequest [ diff --git a/src/MCP/MCPToolSearchRepositories.class.st b/src/MCP/MCPToolSearchRepositories.class.st index bfbb11e..52367a8 100644 --- a/src/MCP/MCPToolSearchRepositories.class.st +++ b/src/MCP/MCPToolSearchRepositories.class.st @@ -17,6 +17,12 @@ MCPToolSearchRepositories class >> toolName [ ^ 'repository_search' ] +{ #category : 'metadata' } +MCPToolSearchRepositories >> additionalKeywords [ + + ^ #( 'repository' 'repo' 'iceberg' 'git' 'branch' 'commit' 'remote' 'head' ) +] + { #category : 'metadata' } MCPToolSearchRepositories >> buildInputSchema [ @@ -102,6 +108,12 @@ MCPToolSearchRepositories >> fieldTextsForRepositoryEntry: anEntry fieldName: fi ^ #( ) ] +{ #category : 'metadata' } +MCPToolSearchRepositories >> groupName [ + + ^ 'repositories' +] + { #category : 'private - filtering' } MCPToolSearchRepositories >> matchesFieldFiltersOnRepositoryEntry: anEntry queryRequest: queryRequest [ diff --git a/src/MCP/MCPToolSearchTools.class.st b/src/MCP/MCPToolSearchTools.class.st index e646631..5add1bc 100644 --- a/src/MCP/MCPToolSearchTools.class.st +++ b/src/MCP/MCPToolSearchTools.class.st @@ -59,6 +59,12 @@ MCPToolSearchTools >> commandForRequest: searchRequest [ ^ MCPSearchToolsCommand tool: self request: searchRequest ] +{ #category : 'metadata' } +MCPToolSearchTools >> defaultExposure [ + + ^ 'static' +] + { #category : 'metadata' } MCPToolSearchTools >> description [ @@ -91,6 +97,12 @@ MCPToolSearchTools >> executeWithRequest: request usingPolicy: aToolExposurePoli onError: [ :error :parsedRequest | self errorResultFor: error ] ] +{ #category : 'metadata' } +MCPToolSearchTools >> groupName [ + + ^ 'tool-catalog' +] + { #category : 'private - request' } MCPToolSearchTools >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolUpdateClassLayout.class.st b/src/MCP/MCPToolUpdateClassLayout.class.st index 87b24cf..7c13be9 100644 --- a/src/MCP/MCPToolUpdateClassLayout.class.st +++ b/src/MCP/MCPToolUpdateClassLayout.class.st @@ -15,6 +15,12 @@ MCPToolUpdateClassLayout class >> toolName [ ^ 'class_layout_update' ] +{ #category : 'metadata' } +MCPToolUpdateClassLayout >> additionalKeywords [ + + ^ super additionalKeywords , #( 'layout' 'shape' 'format' 'fixed' 'variable' 'weak' 'immediate' 'word' 'byte' ) +] + { #category : 'private - specs' } MCPToolUpdateClassLayout >> classToolSpec [ diff --git a/src/MCP/MCPToolUpdateClassSharedPools.class.st b/src/MCP/MCPToolUpdateClassSharedPools.class.st index 37d97f9..9c7d6e5 100644 --- a/src/MCP/MCPToolUpdateClassSharedPools.class.st +++ b/src/MCP/MCPToolUpdateClassSharedPools.class.st @@ -15,6 +15,12 @@ MCPToolUpdateClassSharedPools class >> toolName [ ^ 'class_shared_pools_update' ] +{ #category : 'metadata' } +MCPToolUpdateClassSharedPools >> additionalKeywords [ + + ^ super additionalKeywords , #( 'pool' 'shared' 'pooldictionary' 'dictionary' ) +] + { #category : 'private - specs' } MCPToolUpdateClassSharedPools >> classToolSpec [ diff --git a/src/MCP/MCPToolUpdateClassSharedVariables.class.st b/src/MCP/MCPToolUpdateClassSharedVariables.class.st index fa7cd0c..287ec9e 100644 --- a/src/MCP/MCPToolUpdateClassSharedVariables.class.st +++ b/src/MCP/MCPToolUpdateClassSharedVariables.class.st @@ -15,6 +15,12 @@ MCPToolUpdateClassSharedVariables class >> toolName [ ^ 'class_shared_variables_update' ] +{ #category : 'metadata' } +MCPToolUpdateClassSharedVariables >> additionalKeywords [ + + ^ super additionalKeywords , #( 'variable' 'shared' 'classvar' 'binding' ) +] + { #category : 'private - specs' } MCPToolUpdateClassSharedVariables >> classToolSpec [ diff --git a/src/MCP/MCPToolUpdateClassSideTraits.class.st b/src/MCP/MCPToolUpdateClassSideTraits.class.st index 4ac1fd2..3598643 100644 --- a/src/MCP/MCPToolUpdateClassSideTraits.class.st +++ b/src/MCP/MCPToolUpdateClassSideTraits.class.st @@ -15,6 +15,12 @@ MCPToolUpdateClassSideTraits class >> toolName [ ^ 'class_side_traits_update' ] +{ #category : 'metadata' } +MCPToolUpdateClassSideTraits >> additionalKeywords [ + + ^ super additionalKeywords , #( 'trait' 'side' 'metaclass' 'composition' 'definition' ) +] + { #category : 'private - specs' } MCPToolUpdateClassSideTraits >> classToolSpec [ diff --git a/src/MCP/MCPToolUpdateClassTraits.class.st b/src/MCP/MCPToolUpdateClassTraits.class.st index 7cb5e65..0ab4ca6 100644 --- a/src/MCP/MCPToolUpdateClassTraits.class.st +++ b/src/MCP/MCPToolUpdateClassTraits.class.st @@ -15,6 +15,12 @@ MCPToolUpdateClassTraits class >> toolName [ ^ 'class_traits_update' ] +{ #category : 'metadata' } +MCPToolUpdateClassTraits >> additionalKeywords [ + + ^ super additionalKeywords , #( 'trait' 'composition' 'definition' ) +] + { #category : 'private - specs' } MCPToolUpdateClassTraits >> classToolSpec [ diff --git a/src/MCP/MCPToolUpdateMethodSelector.class.st b/src/MCP/MCPToolUpdateMethodSelector.class.st index 2ac167d..d770dc9 100644 --- a/src/MCP/MCPToolUpdateMethodSelector.class.st +++ b/src/MCP/MCPToolUpdateMethodSelector.class.st @@ -15,6 +15,12 @@ MCPToolUpdateMethodSelector class >> toolName [ ^ 'method_selector_update' ] +{ #category : 'metadata' } +MCPToolUpdateMethodSelector >> defaultExposure [ + + ^ 'static' +] + { #category : 'private - specs' } MCPToolUpdateMethodSelector >> methodToolSpec [ diff --git a/src/MCP/MCPToolVerifyRepositoryIdentity.class.st b/src/MCP/MCPToolVerifyRepositoryIdentity.class.st index e861c41..0a312e4 100644 --- a/src/MCP/MCPToolVerifyRepositoryIdentity.class.st +++ b/src/MCP/MCPToolVerifyRepositoryIdentity.class.st @@ -15,6 +15,18 @@ MCPToolVerifyRepositoryIdentity class >> toolName [ ^ 'repository_identity_verify' ] +{ #category : 'metadata' } +MCPToolVerifyRepositoryIdentity >> additionalKeywords [ + + ^ super additionalKeywords , #( 'identity' 'guard' ) +] + +{ #category : 'metadata' } +MCPToolVerifyRepositoryIdentity >> mutability [ + + ^ 'read' +] + { #category : 'private - specs' } MCPToolVerifyRepositoryIdentity >> repositoryToolSpec [ @@ -33,3 +45,9 @@ MCPToolVerifyRepositoryIdentity >> repositoryToolSpec [ self modifiedPackageNamesSchemaProperty. self isModifiedSchemaProperty }) } asDictionary ] + +{ #category : 'metadata' } +MCPToolVerifyRepositoryIdentity >> risk [ + + ^ 'low' +] From 9a51c8e29feec3422d70873a8cc08b8f3a32b3f2 Mon Sep 17 00:00:00 2001 From: Gabriel-Darbord Date: Mon, 20 Jul 2026 10:33:56 +0200 Subject: [PATCH 2/5] Make tool registry own exposure policy --- .../MCPSaveImageRecordingServer.class.st | 2 +- src/MCP-Tests/MCPToolAPITestCase.class.st | 2 +- src/MCP-Tests/MCPToolContractsTest.class.st | 63 +++++++++---- src/MCP/MCP.class.st | 54 +++++------ src/MCP/MCPGetToolCommand.class.st | 11 +-- src/MCP/MCPSearchToolsCommand.class.st | 7 +- src/MCP/MCPTool.class.st | 15 ++- src/MCP/MCPToolCatalogCommand.class.st | 17 ++-- src/MCP/MCPToolExposurePolicy.class.st | 76 +++++++-------- src/MCP/MCPToolGetTool.class.st | 44 +++++---- src/MCP/MCPToolRegistry.class.st | 93 +++++++++++++++---- src/MCP/MCPToolSearchTools.class.st | 45 +++++---- 12 files changed, 245 insertions(+), 184 deletions(-) diff --git a/src/MCP-Tests/MCPSaveImageRecordingServer.class.st b/src/MCP-Tests/MCPSaveImageRecordingServer.class.st index bc440e7..380f9a1 100644 --- a/src/MCP-Tests/MCPSaveImageRecordingServer.class.st +++ b/src/MCP-Tests/MCPSaveImageRecordingServer.class.st @@ -28,7 +28,7 @@ MCPSaveImageRecordingServer >> didSaveImageSession [ { #category : 'private - calling' } MCPSaveImageRecordingServer >> rpcToolCall: aToolName withParams: someArguments [ - (self toolExposurePolicy isStaticTool: aToolName) ifTrue: [ ^ super rpcToolCall: aToolName withParams: someArguments ]. + (self toolRegistry isStaticToolNamed: aToolName) ifTrue: [ ^ super rpcToolCall: aToolName withParams: someArguments ]. ^ super rpcToolCall: 'tool_call' withParams: { (#toolName -> aToolName). (#arguments -> someArguments) } asDictionary diff --git a/src/MCP-Tests/MCPToolAPITestCase.class.st b/src/MCP-Tests/MCPToolAPITestCase.class.st index 9bcab12..a4036b7 100644 --- a/src/MCP-Tests/MCPToolAPITestCase.class.st +++ b/src/MCP-Tests/MCPToolAPITestCase.class.st @@ -53,7 +53,7 @@ MCPToolAPITestCase >> callToolNamed: aToolName withArguments: someArguments [ | mcp | mcp := self mcpWithoutObservabilityExport. - (mcp toolExposurePolicy isStaticTool: aToolName) ifTrue: [ ^ self callRawToolNamed: aToolName withArguments: someArguments ]. + (mcp toolRegistry isStaticToolNamed: aToolName) ifTrue: [ ^ self callRawToolNamed: aToolName withArguments: someArguments ]. ^ self callDiscoveredToolNamed: aToolName withArguments: someArguments ] diff --git a/src/MCP-Tests/MCPToolContractsTest.class.st b/src/MCP-Tests/MCPToolContractsTest.class.st index 5d85e52..9b2389d 100644 --- a/src/MCP-Tests/MCPToolContractsTest.class.st +++ b/src/MCP-Tests/MCPToolContractsTest.class.st @@ -820,6 +820,15 @@ MCPToolContractsTest >> signalHaltForMCPMessageProcessorTest [ (Smalltalk globals at: #Halt) signal: 'mcp processor halt test' ] +{ #category : 'tests' } +MCPToolContractsTest >> testAllStaticExposurePolicyMakesEveryRegistrationStatic [ + + | registry | + registry := MCPToolRegistry new. + registry exposurePolicy: MCPToolExposurePolicy allStatic. + self assert: registry staticToolNames asSet equals: registry publicToolNames asSet +] + { #category : 'tests' } MCPToolContractsTest >> testAllToolOutputSchemasAdvertiseStructuredSummaryWarningsAndStatus [ @@ -980,6 +989,17 @@ MCPToolContractsTest >> testCaptureScreenshotHasAccurateNameAndSchemas [ self assert: (outputPropertyNames includes: 'mimeType') ] +{ #category : 'tests' } +MCPToolContractsTest >> testCatalogToolsUseOwningRegistry [ + + | registry | + registry := MCPToolRegistry new. + #( 'tool_search' 'tool_get' ) do: [ :toolName | + | tool | + tool := registry toolNamed: toolName ifAbsent: [ self fail ]. + self assert: tool toolRegistry == registry ] +] + { #category : 'tests' } MCPToolContractsTest >> testClassMutationToolsHaveAccurateNamesAndRequiredArguments [ @@ -1412,13 +1432,12 @@ MCPToolContractsTest >> testEvaluateReturnsRuntimeFailureInformation [ { #category : 'tests' } MCPToolContractsTest >> testExposurePolicyOverridesToolDefault [ - | policy registry tool | + | registry tool | registry := MCPToolRegistry new. tool := registry toolNamed: 'method_protocol_update' ifAbsent: [ self fail ]. - policy := MCPToolExposurePolicy forRegistry: registry. - policy staticToolNames: (registry staticToolNames copyWith: tool name). + registry exposurePolicy: (MCPToolExposurePolicy staticToolNames: (registry staticToolNames copyWith: tool name)). self assert: tool defaultExposure equals: 'discoverable'. - self assert: (policy isStaticTool: tool name) + self assert: (registry isStaticToolNamed: tool name) ] { #category : 'tests' } @@ -3126,6 +3145,14 @@ MCPToolContractsTest >> testServerInitializationLeavesIconCacheEmpty [ self assert: MCPStructureIcon cachedRenderedIconCount equals: 0 ] +{ #category : 'tests' } +MCPToolContractsTest >> testServerRegistryOwnsExposurePolicy [ + + | server | + server := self mcpWithoutObservabilityExport. + self assert: server toolExposurePolicy == server toolRegistry exposurePolicy +] + { #category : 'tests' } MCPToolContractsTest >> testSingularSearchTermsUseGenericInflectionRules [ @@ -3185,20 +3212,6 @@ MCPToolContractsTest >> testStringArrayItemDescriptionsStayUseful [ equals: 'Smalltalk expression.' ] -{ #category : 'tests' } -MCPToolContractsTest >> testToolExposurePolicyUsesDefaultsUntilExplicitStaticListProvided [ - - | policy | - self assert: - (MCPToolExposurePolicy default isStaticTool: 'tool_search'). - self deny: - (MCPToolExposurePolicy default isStaticTool: 'repository_search'). - policy := MCPToolExposurePolicy staticToolNames: - #( 'repository_search' ). - self deny: (policy isStaticTool: 'tool_search'). - self assert: (policy isStaticTool: 'repository_search') -] - { #category : 'tests' } MCPToolContractsTest >> testToolFlowSelectorNamesSeparateTransportAndParsedInput [ @@ -3259,7 +3272,7 @@ MCPToolContractsTest >> testToolRegistryAcceptsPluginToolRegistration [ registration := registry registerToolClass: MCPHaltingTestTool. tool := registry toolNamed: MCPHaltingTestTool toolName ifAbsent: [ self fail ]. self assert: tool == registration tool. - self deny: ((MCPToolExposurePolicy forRegistry: registry) isStaticTool: tool name). + self deny: (registry isStaticToolNamed: tool name). self assert: ((registry publicToolGroups detect: [ :each | each key = 'plugins' ] ifNone: [ self fail ]) value includes: MCPHaltingTestTool) ] @@ -3338,6 +3351,18 @@ MCPToolContractsTest >> testToolRegistryReusesRegisteredToolInstances [ self assert: (searchResults detect: [ :each | each name = 'tool_search' ]) == first ] +{ #category : 'tests' } +MCPToolContractsTest >> testToolRegistryUsesDefaultExposureUntilStaticListProvided [ + + | registry | + registry := MCPToolRegistry new. + self assert: (registry isStaticToolNamed: 'tool_search'). + self deny: (registry isStaticToolNamed: 'repository_search'). + registry staticToolNames: #( 'repository_search' ). + self deny: (registry isStaticToolNamed: 'tool_search'). + self assert: (registry isStaticToolNamed: 'repository_search') +] + { #category : 'tests' } MCPToolContractsTest >> testToolsOwnRegistryMetadata [ diff --git a/src/MCP/MCP.class.st b/src/MCP/MCP.class.st index 7278fde..c9f18c0 100644 --- a/src/MCP/MCP.class.st +++ b/src/MCP/MCP.class.st @@ -1,5 +1,5 @@ " -Top-level MCP server application object. MCP owns the registered tool instances, the HTTP server, and observability state. It exposes the core JSON-RPC MCP handlers, including initialization, tool listing, tool calls, ping, and empty resource listings. Tool calls are delegated to the matching MCPTool after request validation. +Top-level MCP server application object. MCP owns one MCPToolRegistry, the HTTP server, and observability state. The registry owns registered tool instances and their exposure policy. MCP exposes the core JSON-RPC handlers, including initialization, tool listing, tool calls, ping, and empty resource listings. Tool calls are delegated to the matching MCPTool after request validation. " Class { #name : 'MCP', @@ -8,7 +8,7 @@ Class { 'server', 'toolsList', 'monitoring', - 'toolExposurePolicy' + 'toolRegistry' ], #category : 'MCP-Server', #package : 'MCP', @@ -104,35 +104,22 @@ MCP >> ensureServerIsListening [ MCP >> executeTool: tool named: toolName withParams: arguments [ | monitorRecord request result toolResult | - monitorRecord := self monitoring - recordToolCallStart: toolName - input: arguments. + monitorRecord := self monitoring recordToolCallStart: toolName input: arguments. ^ [ request := tool requestFromToolCallArguments: arguments. - result := tool - executeWithRequest: request - usingPolicy: self toolExposurePolicy. + result := tool executeWithRequest: request. toolResult := result asMCPStructureCallToolResult. self saveImageAfterSuccessfulTool: tool result: toolResult. - self monitoring - recordToolCallFinish: monitorRecord - result: toolResult - error: nil. + self monitoring recordToolCallFinish: monitorRecord result: toolResult error: nil. toolResult asJRPCJSON ] on: Exception do: [ :error | | errorResult | error mcpIsJRPCError ifTrue: [ - self monitoring - recordToolCallFinish: monitorRecord - result: nil - error: error. + self monitoring recordToolCallFinish: monitorRecord result: nil error: error. error pass ]. errorResult := tool errorResultFor: error. - self monitoring - recordToolCallFinish: monitorRecord - result: errorResult - error: error. + self monitoring recordToolCallFinish: monitorRecord result: errorResult error: error. errorResult asJRPCJSON ] ] @@ -293,7 +280,7 @@ MCP >> port: aPortNumber [ { #category : 'private - tools' } MCP >> refreshToolsList [ - toolsList := self toolRegistry staticToolsDictionaryUsingPolicy: self toolExposurePolicy. + toolsList := self toolRegistry staticToolsDictionary. ^ toolsList ] @@ -427,15 +414,14 @@ MCP >> start [ { #category : 'configuration' } MCP >> staticToolNames [ - ^ self toolExposurePolicy staticToolNames + ^ self toolRegistry staticToolNames ] { #category : 'configuration' } MCP >> staticToolNames: aCollection [ - self toolExposurePolicy: - (MCPToolExposurePolicy staticToolNames: aCollection). - ^ self + self toolRegistry staticToolNames: aCollection. + self refreshToolsList ] { #category : 'start' } @@ -459,23 +445,27 @@ MCP >> supportedPharoMajorVersions [ { #category : 'accessing' } MCP >> toolExposurePolicy [ - ^ toolExposurePolicy ifNil: [ - toolExposurePolicy := MCPToolExposurePolicy default ] + ^ self toolRegistry exposurePolicy ] { #category : 'accessing' } MCP >> toolExposurePolicy: aToolExposurePolicy [ - toolExposurePolicy := aToolExposurePolicy ifNil: [ - MCPToolExposurePolicy default ]. - self refreshToolsList. - ^ toolExposurePolicy + self toolRegistry exposurePolicy: (aToolExposurePolicy ifNil: [ MCPToolExposurePolicy default ]). + self refreshToolsList ] { #category : 'accessing' } MCP >> toolRegistry [ - ^ self toolExposurePolicy toolRegistry + ^ toolRegistry ifNil: [ toolRegistry := MCPToolRegistry new ] +] + +{ #category : 'accessing' } +MCP >> toolRegistry: aToolRegistry [ + + toolRegistry := aToolRegistry ifNil: [ MCPToolRegistry new ]. + self refreshToolsList ] { #category : 'accessing' } diff --git a/src/MCP/MCPGetToolCommand.class.st b/src/MCP/MCPGetToolCommand.class.st index 09f407a..8fa6097 100644 --- a/src/MCP/MCPGetToolCommand.class.st +++ b/src/MCP/MCPGetToolCommand.class.st @@ -14,18 +14,11 @@ Class { { #category : 'instance creation' } MCPGetToolCommand class >> tool: aTool request: aRequest [ - ^ self - tool: aTool - request: aRequest - toolExposurePolicy: MCPToolExposurePolicy default + ^ self tool: aTool request: aRequest toolRegistry: MCPToolRegistry new ] { #category : 'executing' } MCPGetToolCommand >> execute [ - | catalogTool | - catalogTool := self toolExposurePolicy toolRegistry - toolNamed: request toolName - ifAbsent: [ Error signal: 'Unknown MCP tool: ' , request toolName ]. - ^ { (#tool -> (catalogTool contractUsingPolicy: self toolExposurePolicy includeOutputSchema: false)) } asDictionary + ^ { (#tool -> (self toolRegistry contractForToolNamed: request toolName includeOutputSchema: false)) } asDictionary ] diff --git a/src/MCP/MCPSearchToolsCommand.class.st b/src/MCP/MCPSearchToolsCommand.class.st index 7dfbd78..047e9fd 100644 --- a/src/MCP/MCPSearchToolsCommand.class.st +++ b/src/MCP/MCPSearchToolsCommand.class.st @@ -14,10 +14,7 @@ Class { { #category : 'instance creation' } MCPSearchToolsCommand class >> tool: aTool request: aRequest [ - ^ self - tool: aTool - request: aRequest - toolExposurePolicy: MCPToolExposurePolicy default + ^ self tool: aTool request: aRequest toolRegistry: MCPToolRegistry new ] { #category : 'executing' } @@ -25,7 +22,7 @@ MCPSearchToolsCommand >> execute [ | data matchingTools pageSize visibleTools | pageSize := self pageSize. - matchingTools := self toolExposurePolicy toolRegistry toolsMatchingQuery: request query group: request group. + matchingTools := self toolRegistry toolsMatchingQuery: request query group: request group. visibleTools := self visibleToolsFrom: matchingTools. data := Dictionary new. data at: #tools put: (visibleTools collect: [ :each | each searchMetadata ]) asArray. diff --git a/src/MCP/MCPTool.class.st b/src/MCP/MCPTool.class.st index efbfd44..82a9ef9 100644 --- a/src/MCP/MCPTool.class.st +++ b/src/MCP/MCPTool.class.st @@ -44,6 +44,12 @@ MCPTool class >> isBuiltIn [ ^ self package = MCPTool package ] +{ #category : 'instance creation' } +MCPTool class >> newForRegistry: aToolRegistry [ + + ^ self new +] + { #category : 'scripts' } MCPTool class >> toolGroups [ @@ -139,11 +145,10 @@ MCPTool >> commandForRequest: aToolRequest [ ] { #category : 'metadata' } -MCPTool >> contractUsingPolicy: aToolExposurePolicy includeOutputSchema: includeOutputSchema [ +MCPTool >> contractIncludingOutputSchema: includeOutputSchema [ | activationRequirements data examples | data := self metadata copy. - data at: #exposure put: (aToolExposurePolicy exposureForTool: self). data at: #inputSchema put: self inputSchema asJRPCJSON. includeOutputSchema ifTrue: [ data at: #outputSchema put: self outputSchema asJRPCJSON ]. examples := self examples. @@ -288,12 +293,6 @@ MCPTool >> executeWithRequest: aRequest [ self subclassResponsibility ] -{ #category : 'executing' } -MCPTool >> executeWithRequest: aRequest usingPolicy: aToolExposurePolicy [ - - ^ self executeWithRequest: aRequest -] - { #category : 'error handling' } MCPTool >> failureMessageFor: anError [ diff --git a/src/MCP/MCPToolCatalogCommand.class.st b/src/MCP/MCPToolCatalogCommand.class.st index dc94986..696a603 100644 --- a/src/MCP/MCPToolCatalogCommand.class.st +++ b/src/MCP/MCPToolCatalogCommand.class.st @@ -1,11 +1,11 @@ " -Abstract command for tool-catalog operations that need the current exposure policy. +Abstract command for tool-catalog operations. Catalog commands receive the registry whose tools and effective exposure metadata they query. " Class { #name : 'MCPToolCatalogCommand', #superclass : 'MCPToolRequestCommand', #instVars : [ - 'toolExposurePolicy' + 'toolRegistry' ], #category : 'MCP-Commands', #package : 'MCP', @@ -19,23 +19,22 @@ MCPToolCatalogCommand class >> isAbstract [ ] { #category : 'instance creation' } -MCPToolCatalogCommand class >> tool: aTool request: aRequest toolExposurePolicy: aToolExposurePolicy [ +MCPToolCatalogCommand class >> tool: aTool request: aRequest toolRegistry: aToolRegistry [ ^ self new initializeTool: aTool request: aRequest; - toolExposurePolicy: aToolExposurePolicy; + toolRegistry: aToolRegistry; yourself ] { #category : 'accessing' } -MCPToolCatalogCommand >> toolExposurePolicy [ +MCPToolCatalogCommand >> toolRegistry [ - ^ toolExposurePolicy ifNil: [ MCPToolExposurePolicy default ] + ^ toolRegistry ] { #category : 'accessing' } -MCPToolCatalogCommand >> toolExposurePolicy: aToolExposurePolicy [ +MCPToolCatalogCommand >> toolRegistry: aToolRegistry [ - toolExposurePolicy := aToolExposurePolicy ifNil: [ - MCPToolExposurePolicy default ] + toolRegistry := aToolRegistry ] diff --git a/src/MCP/MCPToolExposurePolicy.class.st b/src/MCP/MCPToolExposurePolicy.class.st index 7572801..627ef5e 100644 --- a/src/MCP/MCPToolExposurePolicy.class.st +++ b/src/MCP/MCPToolExposurePolicy.class.st @@ -1,12 +1,12 @@ " -Per-server visibility policy for one MCPToolRegistry. By default it respects each tool's default exposure; users can replace the static tool-name set without changing tool metadata or another registry. +Pure visibility policy for MCP tool registrations. It applies tool defaults unless configured with an explicit static tool-name set or to expose every registration statically. Registry lookup and configuration validation remain the responsibility of MCPToolRegistry. " Class { #name : 'MCPToolExposurePolicy', #superclass : 'Object', #instVars : [ 'staticToolNames', - 'toolRegistry' + 'exposeAllTools' ], #category : 'MCP-Tools', #package : 'MCP', @@ -16,39 +16,45 @@ Class { { #category : 'instance creation' } MCPToolExposurePolicy class >> allStatic [ - | policy | - policy := self default. - policy staticToolNames: policy toolRegistry publicToolNames. - ^ policy + ^ self new + exposeAllTools; + yourself ] { #category : 'instance creation' } MCPToolExposurePolicy class >> default [ - ^ self forRegistry: MCPToolRegistry new -] - -{ #category : 'instance creation' } -MCPToolExposurePolicy class >> forRegistry: aToolRegistry [ - - ^ self new initializeToolRegistry: aToolRegistry + ^ self new ] { #category : 'instance creation' } MCPToolExposurePolicy class >> staticToolNames: aCollection [ - ^ self default + ^ self new staticToolNames: aCollection; yourself ] -{ #category : 'tools' } -MCPToolExposurePolicy >> exposureForTool: aTool [ +{ #category : 'accessing' } +MCPToolExposurePolicy >> explicitStaticToolNames [ + + ^ staticToolNames +] + +{ #category : 'configuration' } +MCPToolExposurePolicy >> exposeAllTools [ + + exposeAllTools := true. + staticToolNames := nil +] + +{ #category : 'testing' } +MCPToolExposurePolicy >> exposesAllTools [ - ^ self exposureForToolRegistration: (self toolRegistry registrationForTool: aTool ifAbsent: [ ^ 'discoverable' ]) + ^ exposeAllTools ] -{ #category : 'tools' } +{ #category : 'accessing' } MCPToolExposurePolicy >> exposureForToolRegistration: aToolRegistration [ ^ (self isStaticToolRegistration: aToolRegistration) @@ -63,43 +69,23 @@ MCPToolExposurePolicy >> hasExplicitStaticToolNames [ ] { #category : 'initialization' } -MCPToolExposurePolicy >> initializeToolRegistry: aToolRegistry [ +MCPToolExposurePolicy >> initialize [ - toolRegistry := aToolRegistry -] - -{ #category : 'testing' } -MCPToolExposurePolicy >> isStaticTool: aToolName [ - - ^ self isStaticToolRegistration: (self toolRegistry registrationNamed: aToolName ifAbsent: [ ^ false ]) + super initialize. + exposeAllTools := false ] { #category : 'testing' } MCPToolExposurePolicy >> isStaticToolRegistration: aToolRegistration [ - self hasExplicitStaticToolNames ifTrue: [ - ^ staticToolNames includes: aToolRegistration name ]. + self exposesAllTools ifTrue: [ ^ true ]. + self hasExplicitStaticToolNames ifTrue: [ ^ staticToolNames includes: aToolRegistration name ]. ^ aToolRegistration isStaticByDefault ] -{ #category : 'accessing' } -MCPToolExposurePolicy >> staticToolNames [ - - ^ staticToolNames ifNil: [ self toolRegistry staticToolNames ] -] - { #category : 'accessing' } MCPToolExposurePolicy >> staticToolNames: aCollection [ - | normalized unknown | - normalized := (aCollection collect: [ :each | each asString ]) asArray. - unknown := normalized reject: [ :each | self toolRegistry includesToolNamed: each ]. - unknown ifNotEmpty: [ Error signal: 'Unknown MCP tool(s) in exposure policy: ' , unknown printString ]. - staticToolNames := normalized -] - -{ #category : 'accessing' } -MCPToolExposurePolicy >> toolRegistry [ - - ^ toolRegistry ifNil: [ toolRegistry := MCPToolRegistry new ] + staticToolNames := (aCollection collect: [ :each | each asString ]) asArray. + exposeAllTools := false ] diff --git a/src/MCP/MCPToolGetTool.class.st b/src/MCP/MCPToolGetTool.class.st index 18f6ed7..29f22f1 100644 --- a/src/MCP/MCPToolGetTool.class.st +++ b/src/MCP/MCPToolGetTool.class.st @@ -1,16 +1,27 @@ " -Static bridge tool for reading a catalog tool's full contract. +Static bridge tool for reading a tool contract from its owning registry. Agents use it after tool_search and before tool_call when a discoverable tool requires exact schema details. " Class { #name : 'MCPToolGetTool', #superclass : 'MCPTool', + #instVars : [ + 'toolRegistry' + ], #category : 'MCP-Tools', #package : 'MCP', #tag : 'Tools' } +{ #category : 'instance creation' } +MCPToolGetTool class >> newForRegistry: aToolRegistry [ + + ^ self new + toolRegistry: aToolRegistry; + yourself +] + { #category : 'metadata' } MCPToolGetTool class >> toolName [ @@ -91,7 +102,7 @@ MCPToolGetTool >> buildOutputSchema [ { #category : 'private - execution' } MCPToolGetTool >> commandForRequest: getRequest [ - ^ MCPGetToolCommand tool: self request: getRequest + ^ MCPGetToolCommand tool: self request: getRequest toolRegistry: self toolRegistry ] { #category : 'metadata' } @@ -109,26 +120,13 @@ MCPToolGetTool >> description [ { #category : 'executing' } MCPToolGetTool >> executeWithRequest: request [ - ^ self - executeWithRequest: request - usingPolicy: MCPToolExposurePolicy default -] - -{ #category : 'executing' } -MCPToolGetTool >> executeWithRequest: request usingPolicy: aToolExposurePolicy [ - ^ self executeParsedRequestFrom: request do: [ :getRequest | | contract data | - data := (MCPGetToolCommand - tool: self - request: getRequest - toolExposurePolicy: aToolExposurePolicy) execute. + data := (self commandForRequest: getRequest) execute. contract := data at: #tool. - self - successResultText: 'Tool ' , (contract at: #name) , ' found.' - data: data ] + self successResultText: 'Tool ' , (contract at: #name) , ' found.' data: data ] onError: [ :error :parsedRequest | self errorResultFor: error ] ] @@ -161,3 +159,15 @@ MCPToolGetTool >> title [ ^ 'Get Tool' ] + +{ #category : 'accessing' } +MCPToolGetTool >> toolRegistry [ + + ^ toolRegistry ifNil: [ toolRegistry := MCPToolRegistry new ] +] + +{ #category : 'accessing' } +MCPToolGetTool >> toolRegistry: aToolRegistry [ + + toolRegistry := aToolRegistry +] diff --git a/src/MCP/MCPToolRegistry.class.st b/src/MCP/MCPToolRegistry.class.st index e498555..017fb19 100644 --- a/src/MCP/MCPToolRegistry.class.st +++ b/src/MCP/MCPToolRegistry.class.st @@ -1,25 +1,44 @@ " Instance-scoped catalog of MCP tools. -A registry discovers built-in concrete MCPTool subclasses, owns one reusable instance of each registered tool, and provides lookup, grouping, static-surface selection, and catalog search. Intrinsic metadata belongs to the tools. Visibility overrides belong to MCPToolExposurePolicy. +A registry discovers built-in concrete MCPTool subclasses, owns one reusable instance of each registered tool, owns the exposure policy applied to those registrations, and provides lookup, grouping, static-surface selection, contract composition, and catalog search. Intrinsic metadata belongs to tools; visibility overrides belong to MCPToolExposurePolicy. " Class { #name : 'MCPToolRegistry', #superclass : 'Object', #instVars : [ - 'registrations' + 'registrations', + 'exposurePolicy' ], #category : 'MCP-Tools', #package : 'MCP', #tag : 'Tools' } +{ #category : 'tools' } +MCPToolRegistry >> contractForTool: aTool includeOutputSchema: includeOutputSchema [ + + | contract | + contract := aTool contractIncludingOutputSchema: includeOutputSchema. + contract at: #exposure put: (self exposureForTool: aTool). + ^ contract +] + +{ #category : 'tools' } +MCPToolRegistry >> contractForToolNamed: aToolName includeOutputSchema: includeOutputSchema [ + + | tool | + tool := self toolNamed: aToolName ifAbsent: [ Error signal: 'Unknown MCP tool: ' , aToolName asString ]. + ^ self contractForTool: tool includeOutputSchema: includeOutputSchema +] + { #category : 'registrations' } MCPToolRegistry >> defaultRegistrations [ | tools | - tools := (MCPTool concreteSubclasses select: [ :toolClass | toolClass isBuiltIn ] thenCollect: [ :toolClass | toolClass new ]) - asArray. + tools := (MCPTool concreteSubclasses + select: [ :toolClass | toolClass isBuiltIn ] + thenCollect: [ :toolClass | toolClass newForRegistry: self ]) asArray. tools sort: [ :left :right | left groupName = right groupName ifTrue: [ left name <= right name ] @@ -27,6 +46,27 @@ MCPToolRegistry >> defaultRegistrations [ ^ tools collect: [ :tool | MCPToolRegistration tool: tool ] ] +{ #category : 'tools' } +MCPToolRegistry >> exposureForTool: aTool [ + + | registration | + registration := self registrationForTool: aTool ifAbsent: [ Error signal: 'MCP tool is not registered: ' , aTool name ]. + ^ self exposurePolicy exposureForToolRegistration: registration +] + +{ #category : 'accessing' } +MCPToolRegistry >> exposurePolicy [ + + ^ exposurePolicy +] + +{ #category : 'accessing' } +MCPToolRegistry >> exposurePolicy: aToolExposurePolicy [ + + self validateExposurePolicy: aToolExposurePolicy. + exposurePolicy := aToolExposurePolicy +] + { #category : 'testing' } MCPToolRegistry >> includesToolNamed: aToolName [ @@ -37,9 +77,18 @@ MCPToolRegistry >> includesToolNamed: aToolName [ MCPToolRegistry >> initialize [ super initialize. + exposurePolicy := MCPToolExposurePolicy default. self resetRegistrations ] +{ #category : 'testing' } +MCPToolRegistry >> isStaticToolNamed: aToolName [ + + | registration | + registration := self registrationNamed: aToolName ifAbsent: [ ^ false ]. + ^ self exposurePolicy isStaticToolRegistration: registration +] + { #category : 'tools' } MCPToolRegistry >> publicToolClasses [ @@ -105,7 +154,7 @@ MCPToolRegistry >> registerTool: aTool [ { #category : 'registrations' } MCPToolRegistry >> registerToolClass: aToolClass [ - ^ self registerTool: aToolClass new + ^ self registerTool: (aToolClass newForRegistry: self) ] { #category : 'registrations' } @@ -154,37 +203,31 @@ MCPToolRegistry >> singularSearchTermFor: aTerm [ { #category : 'tools' } MCPToolRegistry >> staticToolNames [ - ^ (self registrations select: [ :each | each isStaticByDefault ] thenCollect: [ :each | each name ]) asArray + ^ (self staticTools collect: [ :each | each name ]) asArray ] -{ #category : 'tools' } -MCPToolRegistry >> staticTools [ +{ #category : 'configuration' } +MCPToolRegistry >> staticToolNames: aCollection [ - ^ self staticToolsUsingPolicy: (MCPToolExposurePolicy forRegistry: self) + self exposurePolicy: (MCPToolExposurePolicy staticToolNames: aCollection) ] { #category : 'tools' } -MCPToolRegistry >> staticToolsDictionary [ +MCPToolRegistry >> staticTools [ - ^ self staticToolsDictionaryUsingPolicy: (MCPToolExposurePolicy forRegistry: self) + ^ (self registrations select: [ :each | self exposurePolicy isStaticToolRegistration: each ] thenCollect: [ :each | each tool ]) + asArray ] { #category : 'tools' } -MCPToolRegistry >> staticToolsDictionaryUsingPolicy: aToolExposurePolicy [ +MCPToolRegistry >> staticToolsDictionary [ | dictionary | dictionary := Dictionary new. - (self staticToolsUsingPolicy: aToolExposurePolicy) do: [ :tool | dictionary at: tool name put: tool ]. + self staticTools do: [ :tool | dictionary at: tool name put: tool ]. ^ dictionary ] -{ #category : 'tools' } -MCPToolRegistry >> staticToolsUsingPolicy: aToolExposurePolicy [ - - ^ (self registrations select: [ :each | aToolExposurePolicy isStaticToolRegistration: each ] thenCollect: [ :each | each tool ]) - asArray -] - { #category : 'tools' } MCPToolRegistry >> tool: aTool matchesQuery: queryString group: groupString [ @@ -217,3 +260,13 @@ MCPToolRegistry >> unregisterToolNamed: aToolName [ toolName := aToolName asString. registrations := self registrations reject: [ :each | each name = toolName ] ] + +{ #category : 'private - exposure' } +MCPToolRegistry >> validateExposurePolicy: aToolExposurePolicy [ + + | staticNames unknown | + staticNames := aToolExposurePolicy explicitStaticToolNames. + staticNames ifNil: [ ^ self ]. + unknown := staticNames reject: [ :each | self includesToolNamed: each ]. + unknown ifNotEmpty: [ Error signal: 'Unknown MCP tool(s) in exposure policy: ' , unknown printString ] +] diff --git a/src/MCP/MCPToolSearchTools.class.st b/src/MCP/MCPToolSearchTools.class.st index 5add1bc..824446b 100644 --- a/src/MCP/MCPToolSearchTools.class.st +++ b/src/MCP/MCPToolSearchTools.class.st @@ -1,16 +1,27 @@ " -Static bridge tool for searching the discoverable tool catalog. +Static bridge tool for searching its owning registry's discoverable tool catalog. It is intentionally kept on the fast path so agents can find optional or specialized tools without requiring those tools to be statically advertised. " Class { #name : 'MCPToolSearchTools', #superclass : 'MCPTool', + #instVars : [ + 'toolRegistry' + ], #category : 'MCP-Tools', #package : 'MCP', #tag : 'Tools' } +{ #category : 'instance creation' } +MCPToolSearchTools class >> newForRegistry: aToolRegistry [ + + ^ self new + toolRegistry: aToolRegistry; + yourself +] + { #category : 'metadata' } MCPToolSearchTools class >> toolName [ @@ -56,7 +67,7 @@ MCPToolSearchTools >> buildOutputSchema [ { #category : 'private - execution' } MCPToolSearchTools >> commandForRequest: searchRequest [ - ^ MCPSearchToolsCommand tool: self request: searchRequest + ^ MCPSearchToolsCommand tool: self request: searchRequest toolRegistry: self toolRegistry ] { #category : 'metadata' } @@ -74,26 +85,12 @@ MCPToolSearchTools >> description [ { #category : 'executing' } MCPToolSearchTools >> executeWithRequest: request [ - ^ self - executeWithRequest: request - usingPolicy: MCPToolExposurePolicy default -] - -{ #category : 'executing' } -MCPToolSearchTools >> executeWithRequest: request usingPolicy: aToolExposurePolicy [ - ^ self executeParsedRequestFrom: request do: [ :searchRequest | | data | - data := (MCPSearchToolsCommand - tool: self - request: searchRequest - toolExposurePolicy: aToolExposurePolicy) execute. - self - successResultText: - (data at: #tools) size asString , ' tools found.' - data: data ] + data := (self commandForRequest: searchRequest) execute. + self successResultText: (data at: #tools) size asString , ' tools found.' data: data ] onError: [ :error :parsedRequest | self errorResultFor: error ] ] @@ -135,6 +132,18 @@ MCPToolSearchTools >> toolMetadataObjectSchema [ yourself ] +{ #category : 'accessing' } +MCPToolSearchTools >> toolRegistry [ + + ^ toolRegistry ifNil: [ toolRegistry := MCPToolRegistry new ] +] + +{ #category : 'accessing' } +MCPToolSearchTools >> toolRegistry: aToolRegistry [ + + toolRegistry := aToolRegistry +] + { #category : 'private - schema' } MCPToolSearchTools >> toolsArraySchemaProperty [ From e529a2915fec73b62dfe631cc701df6685e7c237 Mon Sep 17 00:00:00 2001 From: Gabriel Darbord <78592838+Gabriel-Darbord@users.noreply.github.com> Date: Mon, 20 Jul 2026 10:49:24 +0200 Subject: [PATCH 3/5] Remove unused tool risk metadata --- src/MCP-Tests/MCPToolChangeHistoryToolsTest.class.st | 2 +- src/MCP-Tests/MCPToolContractsTest.class.st | 12 ++++-------- src/MCP-Tests/MCPToolMethodLookupTest.class.st | 6 ++---- src/MCP-Tests/MCPToolRepositoryToolsTest.class.st | 2 +- src/MCP/MCPTool.class.st | 7 ------- src/MCP/MCPToolCallTool.class.st | 6 ------ src/MCP/MCPToolCaptureScreenshot.class.st | 6 ------ src/MCP/MCPToolDebugCapture.class.st | 6 ------ src/MCP/MCPToolDebugEvaluate.class.st | 6 ------ src/MCP/MCPToolDebugState.class.st | 6 ------ src/MCP/MCPToolDebugTest.class.st | 6 ------ src/MCP/MCPToolDebugVariables.class.st | 6 ------ src/MCP/MCPToolEvaluate.class.st | 6 ------ src/MCP/MCPToolGetClass.class.st | 6 ------ src/MCP/MCPToolGetDebugSession.class.st | 6 ------ src/MCP/MCPToolGetMethod.class.st | 6 ------ src/MCP/MCPToolGetTool.class.st | 6 ------ src/MCP/MCPToolListChangeHistoryEntries.class.st | 6 ------ src/MCP/MCPToolListChangeHistoryFiles.class.st | 6 ------ src/MCP/MCPToolListDebugBreakpoints.class.st | 6 ------ src/MCP/MCPToolListDebugSessions.class.st | 6 ------ src/MCP/MCPToolListRepositoryChanges.class.st | 6 ------ src/MCP/MCPToolLoadBaseline.class.st | 6 ------ src/MCP/MCPToolLoadRepository.class.st | 6 ------ src/MCP/MCPToolRunTests.class.st | 6 ------ src/MCP/MCPToolSearch.class.st | 6 ------ src/MCP/MCPToolSearchDebugCandidates.class.st | 6 ------ src/MCP/MCPToolVerifyRepositoryIdentity.class.st | 6 ------ 28 files changed, 8 insertions(+), 159 deletions(-) diff --git a/src/MCP-Tests/MCPToolChangeHistoryToolsTest.class.st b/src/MCP-Tests/MCPToolChangeHistoryToolsTest.class.st index d0eb3a8..de3548b 100644 --- a/src/MCP-Tests/MCPToolChangeHistoryToolsTest.class.st +++ b/src/MCP-Tests/MCPToolChangeHistoryToolsTest.class.st @@ -16,7 +16,7 @@ MCPToolChangeHistoryToolsTest >> inputPropertyNamesFor: aTool [ ] { #category : 'tests' } -MCPToolChangeHistoryToolsTest >> testChangeHistoryToolsAreDiscoverableWithExpectedRisk [ +MCPToolChangeHistoryToolsTest >> testChangeHistoryToolsAreDiscoverableWithExpectedMetadata [ | metadataByName result toolNames | result := self callToolNamed: 'tool_search' withArguments: { (#group -> 'history') } asDictionary. diff --git a/src/MCP-Tests/MCPToolContractsTest.class.st b/src/MCP-Tests/MCPToolContractsTest.class.st index 9b2389d..cf5962a 100644 --- a/src/MCP-Tests/MCPToolContractsTest.class.st +++ b/src/MCP-Tests/MCPToolContractsTest.class.st @@ -899,15 +899,13 @@ MCPToolContractsTest >> testBrowseIconToolGroupsKeepRelatedToolsTogether [ { #category : 'tests' } MCPToolContractsTest >> testBuiltInToolsProvideCatalogMetadata [ - | validExposures validMutabilities validRisks | + | validExposures validMutabilities | validExposures := #( 'static' 'discoverable' ). validMutabilities := #( 'read' 'execute' 'write' ). - validRisks := #( 'low' 'medium' 'high' ). self toolRegistry publicTools do: [ :tool | self deny: tool groupName equals: 'other'. self assert: (validExposures includes: tool defaultExposure). self assert: (validMutabilities includes: tool mutability). - self assert: (validRisks includes: tool risk). self assert: tool keywords notEmpty ] ] @@ -1346,8 +1344,8 @@ MCPToolContractsTest >> testDiscoveryUsesConfiguredStaticToolNames [ (server rpcToolCall: 'tool_get' withParams: { (#toolName -> 'repository_search') } asDictionary)) at: #tool. createContract := (self dataFrom: (server rpcToolCall: 'tool_get' withParams: { (#toolName -> 'repository_create') } asDictionary)) at: #tool. - self assert: searchContract keys asSet equals: #( name description group exposure mutability risk keywords inputSchema ) asSet. - self assert: createContract keys asSet equals: #( name description group exposure mutability risk keywords inputSchema ) asSet. + self assert: searchContract keys asSet equals: #( name description group exposure mutability keywords inputSchema ) asSet. + self assert: createContract keys asSet equals: #( name description group exposure mutability keywords inputSchema ) asSet. self assert: (searchContract at: #exposure) equals: 'static'. self assert: (createContract at: #exposure) equals: 'discoverable' ] @@ -1490,11 +1488,10 @@ MCPToolContractsTest >> testGetToolReturnsSchemaForCatalogTool [ result := self callToolNamed: 'tool_get' withArguments: { (#toolName -> 'repository_create') } asDictionary. contract := (self dataFrom: result) at: #tool. self assert: (contract at: #name) equals: 'repository_create'. - self assert: contract keys asSet equals: #( name description group exposure mutability risk keywords inputSchema ) asSet. + self assert: contract keys asSet equals: #( name description group exposure mutability keywords inputSchema ) asSet. self assert: (contract at: #group) equals: 'repositories'. self assert: (contract at: #exposure) equals: 'discoverable'. self assert: (contract at: #mutability) equals: 'write'. - self assert: (contract at: #risk) equals: 'high'. self assert: ((contract at: #keywords) includes: 'repo'). inputProperties := ((contract at: #inputSchema) at: #properties) keys. self assert: inputProperties asSet equals: #( 'name' 'location' 'packageNames' 'subdirectory' ) asSet. @@ -3371,7 +3368,6 @@ MCPToolContractsTest >> testToolsOwnRegistryMetadata [ self assert: tool groupName equals: 'runtime'. self assert: tool defaultExposure equals: 'static'. self assert: tool mutability equals: 'execute'. - self assert: tool risk equals: 'medium'. self assert: (tool keywords includes: 'smalltalk') ] diff --git a/src/MCP-Tests/MCPToolMethodLookupTest.class.st b/src/MCP-Tests/MCPToolMethodLookupTest.class.st index 1ae1cb9..91331f0 100644 --- a/src/MCP-Tests/MCPToolMethodLookupTest.class.st +++ b/src/MCP-Tests/MCPToolMethodLookupTest.class.st @@ -65,16 +65,14 @@ MCPToolMethodLookupTest >> testLookupToolsArePublicStaticReadTools [ self assert: (self toolRegistry staticToolNames includes: each). tool := self toolRegistry toolNamed: each ifAbsent: [ self fail: 'Missing tool ' , each ]. self assert: tool defaultExposure equals: 'static'. - self assert: tool mutability equals: 'read'. - self assert: tool risk equals: 'low' ]. + self assert: tool mutability equals: 'read' ]. discoverableNames do: [ :each | | tool | self assert: (self toolRegistry publicToolNames includes: each). self deny: (self toolRegistry staticToolNames includes: each). tool := self toolRegistry toolNamed: each ifAbsent: [ self fail: 'Missing tool ' , each ]. self assert: tool defaultExposure equals: 'discoverable'. - self assert: tool mutability equals: 'read'. - self assert: tool risk equals: 'low' ] + self assert: tool mutability equals: 'read' ] ] { #category : 'tests' } diff --git a/src/MCP-Tests/MCPToolRepositoryToolsTest.class.st b/src/MCP-Tests/MCPToolRepositoryToolsTest.class.st index 71d698f..9c92157 100644 --- a/src/MCP-Tests/MCPToolRepositoryToolsTest.class.st +++ b/src/MCP-Tests/MCPToolRepositoryToolsTest.class.st @@ -171,7 +171,7 @@ MCPToolRepositoryToolsTest >> repositoryToolSpecifications [ ] { #category : 'tests' } -MCPToolRepositoryToolsTest >> testRepositoryToolsAreDiscoverableWithExpectedRisk [ +MCPToolRepositoryToolsTest >> testRepositoryToolsAreDiscoverableWithExpectedMetadata [ | metadataByName | metadataByName := self metadataByNameForRepositoryTools. diff --git a/src/MCP/MCPTool.class.st b/src/MCP/MCPTool.class.st index 82a9ef9..3a63ad2 100644 --- a/src/MCP/MCPTool.class.st +++ b/src/MCP/MCPTool.class.st @@ -372,7 +372,6 @@ MCPTool >> metadata [ (#description -> self description). (#group -> self groupName). (#mutability -> self mutability). - (#risk -> self risk). (#keywords -> self keywords) } asDictionary ] @@ -432,12 +431,6 @@ MCPTool >> requestFromToolCallArguments: toolCallArguments [ ^ MCPToolRequest tool: self arguments: toolCallArguments ] -{ #category : 'metadata' } -MCPTool >> risk [ - - ^ 'high' -] - { #category : 'private - schema' } MCPTool >> schemaPropertyNamed: aName type: aType description: aDescription [ diff --git a/src/MCP/MCPToolCallTool.class.st b/src/MCP/MCPToolCallTool.class.st index 7c74174..2bc7e87 100644 --- a/src/MCP/MCPToolCallTool.class.st +++ b/src/MCP/MCPToolCallTool.class.st @@ -110,12 +110,6 @@ MCPToolCallTool >> parsedRequestFromToolRequest: request [ ^ MCPCallToolRequest fromToolRequest: request ] -{ #category : 'metadata' } -MCPToolCallTool >> risk [ - - ^ 'medium' -] - { #category : 'metadata' } MCPToolCallTool >> title [ diff --git a/src/MCP/MCPToolCaptureScreenshot.class.st b/src/MCP/MCPToolCaptureScreenshot.class.st index 63274d4..f56942d 100644 --- a/src/MCP/MCPToolCaptureScreenshot.class.st +++ b/src/MCP/MCPToolCaptureScreenshot.class.st @@ -114,12 +114,6 @@ MCPToolCaptureScreenshot >> parsedRequestFromToolRequest: request [ ^ MCPScreenshotRequest fromRequest: request tool: self ] -{ #category : 'metadata' } -MCPToolCaptureScreenshot >> risk [ - - ^ 'low' -] - { #category : 'private - results' } MCPToolCaptureScreenshot >> successSummaryForResult: aScreenshotResult [ diff --git a/src/MCP/MCPToolDebugCapture.class.st b/src/MCP/MCPToolDebugCapture.class.st index 46a8238..576bab3 100644 --- a/src/MCP/MCPToolDebugCapture.class.st +++ b/src/MCP/MCPToolDebugCapture.class.st @@ -151,12 +151,6 @@ MCPToolDebugCapture >> parsedRequestFromToolRequest: request [ ^ MCPDebugCaptureRequest fromRequest: request tool: self ] -{ #category : 'metadata' } -MCPToolDebugCapture >> risk [ - - ^ 'medium' -] - { #category : 'testing' } MCPToolDebugCapture >> shouldSaveImageAfterSuccessfulExecution [ diff --git a/src/MCP/MCPToolDebugEvaluate.class.st b/src/MCP/MCPToolDebugEvaluate.class.st index 35e3ee1..681f758 100644 --- a/src/MCP/MCPToolDebugEvaluate.class.st +++ b/src/MCP/MCPToolDebugEvaluate.class.st @@ -140,12 +140,6 @@ MCPToolDebugEvaluate >> resultPreviewCharacterLimit [ ^ 10000 ] -{ #category : 'metadata' } -MCPToolDebugEvaluate >> risk [ - - ^ 'medium' -] - { #category : 'private' } MCPToolDebugEvaluate >> runtimeFailureMessageFor: anError [ diff --git a/src/MCP/MCPToolDebugState.class.st b/src/MCP/MCPToolDebugState.class.st index 1526dbf..95dc95b 100644 --- a/src/MCP/MCPToolDebugState.class.st +++ b/src/MCP/MCPToolDebugState.class.st @@ -163,12 +163,6 @@ MCPToolDebugState >> parsedRequestFromToolRequest: request [ ^ MCPDebugStateRequest fromRequest: request tool: self ] -{ #category : 'metadata' } -MCPToolDebugState >> risk [ - - ^ 'low' -] - { #category : 'metadata' } MCPToolDebugState >> title [ diff --git a/src/MCP/MCPToolDebugTest.class.st b/src/MCP/MCPToolDebugTest.class.st index fc4aac2..f6e57c4 100644 --- a/src/MCP/MCPToolDebugTest.class.st +++ b/src/MCP/MCPToolDebugTest.class.st @@ -166,12 +166,6 @@ MCPToolDebugTest >> parsedRequestFromToolRequest: request [ ^ MCPDebugTestRequest fromRequest: request tool: self ] -{ #category : 'metadata' } -MCPToolDebugTest >> risk [ - - ^ 'medium' -] - { #category : 'testing' } MCPToolDebugTest >> shouldSaveImageAfterSuccessfulExecution [ diff --git a/src/MCP/MCPToolDebugVariables.class.st b/src/MCP/MCPToolDebugVariables.class.st index 59e0e06..52233d0 100644 --- a/src/MCP/MCPToolDebugVariables.class.st +++ b/src/MCP/MCPToolDebugVariables.class.st @@ -139,12 +139,6 @@ MCPToolDebugVariables >> parsedRequestFromToolRequest: request [ ^ MCPDebugVariablesRequest fromRequest: request tool: self ] -{ #category : 'metadata' } -MCPToolDebugVariables >> risk [ - - ^ 'low' -] - { #category : 'metadata' } MCPToolDebugVariables >> title [ diff --git a/src/MCP/MCPToolEvaluate.class.st b/src/MCP/MCPToolEvaluate.class.st index 767906c..811e4cd 100644 --- a/src/MCP/MCPToolEvaluate.class.st +++ b/src/MCP/MCPToolEvaluate.class.st @@ -126,12 +126,6 @@ MCPToolEvaluate >> resultPreviewCharacterLimit [ ^ 10000 ] -{ #category : 'metadata' } -MCPToolEvaluate >> risk [ - - ^ 'medium' -] - { #category : 'private' } MCPToolEvaluate >> runtimeFailureMessageFor: anError [ diff --git a/src/MCP/MCPToolGetClass.class.st b/src/MCP/MCPToolGetClass.class.st index b63a5f3..973e9f0 100644 --- a/src/MCP/MCPToolGetClass.class.st +++ b/src/MCP/MCPToolGetClass.class.st @@ -306,12 +306,6 @@ MCPToolGetClass >> parsedRequestFromToolRequest: request [ ^ MCPGetClassRequest fromRequest: request tool: self ] -{ #category : 'metadata' } -MCPToolGetClass >> risk [ - - ^ 'low' -] - { #category : 'private' } MCPToolGetClass >> successSummaryForClassName: className superclasses: superclasses subclasses: subclasses [ diff --git a/src/MCP/MCPToolGetDebugSession.class.st b/src/MCP/MCPToolGetDebugSession.class.st index cf96614..8aee219 100644 --- a/src/MCP/MCPToolGetDebugSession.class.st +++ b/src/MCP/MCPToolGetDebugSession.class.st @@ -21,12 +21,6 @@ MCPToolGetDebugSession >> mutability [ ^ 'read' ] -{ #category : 'metadata' } -MCPToolGetDebugSession >> risk [ - - ^ 'low' -] - { #category : 'private - specs' } MCPToolGetDebugSession >> sessionToolSpec [ diff --git a/src/MCP/MCPToolGetMethod.class.st b/src/MCP/MCPToolGetMethod.class.st index 77a1e04..6644d1b 100644 --- a/src/MCP/MCPToolGetMethod.class.st +++ b/src/MCP/MCPToolGetMethod.class.st @@ -272,12 +272,6 @@ MCPToolGetMethod >> parsedRequestFromToolRequest: request [ ^ MCPGetMethodRequest fromRequest: request tool: self ] -{ #category : 'metadata' } -MCPToolGetMethod >> risk [ - - ^ 'low' -] - { #category : 'private - errors' } MCPToolGetMethod >> selectorForErrorFromParsedRequest: getRequest rawRequest: rawRequest [ diff --git a/src/MCP/MCPToolGetTool.class.st b/src/MCP/MCPToolGetTool.class.st index 29f22f1..d77e0c5 100644 --- a/src/MCP/MCPToolGetTool.class.st +++ b/src/MCP/MCPToolGetTool.class.st @@ -148,12 +148,6 @@ MCPToolGetTool >> parsedRequestFromToolRequest: request [ ^ MCPGetToolRequest fromToolRequest: request ] -{ #category : 'metadata' } -MCPToolGetTool >> risk [ - - ^ 'low' -] - { #category : 'metadata' } MCPToolGetTool >> title [ diff --git a/src/MCP/MCPToolListChangeHistoryEntries.class.st b/src/MCP/MCPToolListChangeHistoryEntries.class.st index eefdedd..8247ed5 100644 --- a/src/MCP/MCPToolListChangeHistoryEntries.class.st +++ b/src/MCP/MCPToolListChangeHistoryEntries.class.st @@ -103,12 +103,6 @@ MCPToolListChangeHistoryEntries >> parsedRequestFromToolRequest: request [ ^ MCPChangeHistoryListEntriesRequest fromRequest: request tool: self ] -{ #category : 'metadata' } -MCPToolListChangeHistoryEntries >> risk [ - - ^ 'low' -] - { #category : 'metadata' } MCPToolListChangeHistoryEntries >> title [ diff --git a/src/MCP/MCPToolListChangeHistoryFiles.class.st b/src/MCP/MCPToolListChangeHistoryFiles.class.st index 6bf92ad..73a6c79 100644 --- a/src/MCP/MCPToolListChangeHistoryFiles.class.st +++ b/src/MCP/MCPToolListChangeHistoryFiles.class.st @@ -98,12 +98,6 @@ MCPToolListChangeHistoryFiles >> parsedRequestFromToolRequest: request [ ^ MCPChangeHistoryListLogsRequest fromRequest: request tool: self ] -{ #category : 'metadata' } -MCPToolListChangeHistoryFiles >> risk [ - - ^ 'low' -] - { #category : 'metadata' } MCPToolListChangeHistoryFiles >> title [ diff --git a/src/MCP/MCPToolListDebugBreakpoints.class.st b/src/MCP/MCPToolListDebugBreakpoints.class.st index fd350a2..77cfe95 100644 --- a/src/MCP/MCPToolListDebugBreakpoints.class.st +++ b/src/MCP/MCPToolListDebugBreakpoints.class.st @@ -30,9 +30,3 @@ MCPToolListDebugBreakpoints >> mutability [ ^ 'read' ] - -{ #category : 'metadata' } -MCPToolListDebugBreakpoints >> risk [ - - ^ 'low' -] diff --git a/src/MCP/MCPToolListDebugSessions.class.st b/src/MCP/MCPToolListDebugSessions.class.st index fc0a058..b1ee19c 100644 --- a/src/MCP/MCPToolListDebugSessions.class.st +++ b/src/MCP/MCPToolListDebugSessions.class.st @@ -21,12 +21,6 @@ MCPToolListDebugSessions >> mutability [ ^ 'read' ] -{ #category : 'metadata' } -MCPToolListDebugSessions >> risk [ - - ^ 'low' -] - { #category : 'private - specs' } MCPToolListDebugSessions >> sessionToolSpec [ diff --git a/src/MCP/MCPToolListRepositoryChanges.class.st b/src/MCP/MCPToolListRepositoryChanges.class.st index 73ee73a..45eb16b 100644 --- a/src/MCP/MCPToolListRepositoryChanges.class.st +++ b/src/MCP/MCPToolListRepositoryChanges.class.st @@ -39,9 +39,3 @@ MCPToolListRepositoryChanges >> repositoryToolSpec [ (#requestClass -> MCPRepositoryDiffRequest). (#commandClass -> MCPRepositoryDiffCommand) } asDictionary ] - -{ #category : 'metadata' } -MCPToolListRepositoryChanges >> risk [ - - ^ 'low' -] diff --git a/src/MCP/MCPToolLoadBaseline.class.st b/src/MCP/MCPToolLoadBaseline.class.st index 816b007..3674593 100644 --- a/src/MCP/MCPToolLoadBaseline.class.st +++ b/src/MCP/MCPToolLoadBaseline.class.st @@ -60,12 +60,6 @@ MCPToolLoadBaseline >> parsedRequestFromToolRequest: request [ ^ MCPLoadBaselineRequest fromRequest: request tool: self ] -{ #category : 'metadata' } -MCPToolLoadBaseline >> risk [ - - ^ 'medium' -] - { #category : 'metadata' } MCPToolLoadBaseline >> title [ diff --git a/src/MCP/MCPToolLoadRepository.class.st b/src/MCP/MCPToolLoadRepository.class.st index 0c3b329..11f081e 100644 --- a/src/MCP/MCPToolLoadRepository.class.st +++ b/src/MCP/MCPToolLoadRepository.class.st @@ -225,12 +225,6 @@ MCPToolLoadRepository >> repositoryUrlSchemaProperty [ yourself ] -{ #category : 'metadata' } -MCPToolLoadRepository >> risk [ - - ^ 'medium' -] - { #category : 'testing' } MCPToolLoadRepository >> shouldSaveImageAfterSuccessfulExecution [ diff --git a/src/MCP/MCPToolRunTests.class.st b/src/MCP/MCPToolRunTests.class.st index c91e73b..b9e786f 100644 --- a/src/MCP/MCPToolRunTests.class.st +++ b/src/MCP/MCPToolRunTests.class.st @@ -326,12 +326,6 @@ MCPToolRunTests >> rawTestNamesFromRequest: rawRequest [ fromRequest: rawRequest) do: [ :each | stream nextPut: each ] ] ] ] -{ #category : 'metadata' } -MCPToolRunTests >> risk [ - - ^ 'medium' -] - { #category : 'private' } MCPToolRunTests >> successSummaryForData: data [ diff --git a/src/MCP/MCPToolSearch.class.st b/src/MCP/MCPToolSearch.class.st index 2ea30e3..0a3220b 100644 --- a/src/MCP/MCPToolSearch.class.st +++ b/src/MCP/MCPToolSearch.class.st @@ -522,12 +522,6 @@ MCPToolSearch >> requestedContextFromRequest: request [ ^ self subclassResponsibility ] -{ #category : 'metadata' } -MCPToolSearch >> risk [ - - ^ 'low' -] - { #category : 'private - results' } MCPToolSearch >> scopeQueryFailureMessageFor: resultKind scope: scopeSummary error: anError [ diff --git a/src/MCP/MCPToolSearchDebugCandidates.class.st b/src/MCP/MCPToolSearchDebugCandidates.class.st index 10d37aa..3850edb 100644 --- a/src/MCP/MCPToolSearchDebugCandidates.class.st +++ b/src/MCP/MCPToolSearchDebugCandidates.class.st @@ -21,12 +21,6 @@ MCPToolSearchDebugCandidates >> mutability [ ^ 'read' ] -{ #category : 'metadata' } -MCPToolSearchDebugCandidates >> risk [ - - ^ 'low' -] - { #category : 'private - specs' } MCPToolSearchDebugCandidates >> sessionToolSpec [ diff --git a/src/MCP/MCPToolVerifyRepositoryIdentity.class.st b/src/MCP/MCPToolVerifyRepositoryIdentity.class.st index 0a312e4..7ccaa5e 100644 --- a/src/MCP/MCPToolVerifyRepositoryIdentity.class.st +++ b/src/MCP/MCPToolVerifyRepositoryIdentity.class.st @@ -45,9 +45,3 @@ MCPToolVerifyRepositoryIdentity >> repositoryToolSpec [ self modifiedPackageNamesSchemaProperty. self isModifiedSchemaProperty }) } asDictionary ] - -{ #category : 'metadata' } -MCPToolVerifyRepositoryIdentity >> risk [ - - ^ 'low' -] From ff6bf65cecab5d804c606ac45f3af7acfe26f7b2 Mon Sep 17 00:00:00 2001 From: Gabriel Darbord <78592838+Gabriel-Darbord@users.noreply.github.com> Date: Mon, 20 Jul 2026 11:05:54 +0200 Subject: [PATCH 4/5] Use standard MCP tool annotations --- docs/dev/developing-mcp.md | 2 +- src/MCP-Tests/MCPToolContractsTest.class.st | 38 +++++-- .../MCPToolMethodLookupTest.class.st | 4 +- .../MCPToolStructuredOutputTest.class.st | 2 +- src/MCP/MCPStructureToolAnnotations.class.st | 106 ++++++++++++++++++ src/MCP/MCPTool.class.st | 45 +++----- src/MCP/MCPToolCallTool.class.st | 6 - src/MCP/MCPToolCaptureScreenshot.class.st | 12 +- src/MCP/MCPToolDebugCapture.class.st | 6 - src/MCP/MCPToolDebugEvaluate.class.st | 6 - src/MCP/MCPToolDebugState.class.st | 12 +- src/MCP/MCPToolDebugTest.class.st | 6 - src/MCP/MCPToolDebugVariables.class.st | 12 +- src/MCP/MCPToolEvaluate.class.st | 6 - src/MCP/MCPToolGetClass.class.st | 12 +- src/MCP/MCPToolGetDebugSession.class.st | 4 +- src/MCP/MCPToolGetMethod.class.st | 12 +- src/MCP/MCPToolGetTool.class.st | 105 ++++++++--------- .../MCPToolListChangeHistoryEntries.class.st | 12 +- .../MCPToolListChangeHistoryFiles.class.st | 12 +- src/MCP/MCPToolListDebugBreakpoints.class.st | 12 +- src/MCP/MCPToolListDebugSessions.class.st | 4 +- src/MCP/MCPToolListRepositoryChanges.class.st | 4 +- src/MCP/MCPToolRunTests.class.st | 6 - src/MCP/MCPToolSearch.class.st | 12 +- src/MCP/MCPToolSearchDebugCandidates.class.st | 4 +- src/MCP/MCPToolSearchTools.class.st | 6 + .../MCPToolVerifyRepositoryIdentity.class.st | 4 +- 28 files changed, 276 insertions(+), 196 deletions(-) create mode 100644 src/MCP/MCPStructureToolAnnotations.class.st diff --git a/docs/dev/developing-mcp.md b/docs/dev/developing-mcp.md index aeec21d..b54de35 100644 --- a/docs/dev/developing-mcp.md +++ b/docs/dev/developing-mcp.md @@ -53,7 +53,7 @@ Rules: strings, or broad exception swallowing. - Keep schema and contract tests close to tool behavior changes. - Keep discoverable-tool metadata current when adding or changing tool groups, - keywords, mutability, risk, or schema-inspection expectations. + keywords, standard tool annotations, or schema-inspection expectations. - For debugger tools, keep state references opaque and scoped to the debug state that returned them. - Keep UI changes separate from core MCP tool behavior unless the task diff --git a/src/MCP-Tests/MCPToolContractsTest.class.st b/src/MCP-Tests/MCPToolContractsTest.class.st index cf5962a..8cf685d 100644 --- a/src/MCP-Tests/MCPToolContractsTest.class.st +++ b/src/MCP-Tests/MCPToolContractsTest.class.st @@ -899,13 +899,12 @@ MCPToolContractsTest >> testBrowseIconToolGroupsKeepRelatedToolsTogether [ { #category : 'tests' } MCPToolContractsTest >> testBuiltInToolsProvideCatalogMetadata [ - | validExposures validMutabilities | + | validExposures | validExposures := #( 'static' 'discoverable' ). - validMutabilities := #( 'read' 'execute' 'write' ). self toolRegistry publicTools do: [ :tool | self deny: tool groupName equals: 'other'. self assert: (validExposures includes: tool defaultExposure). - self assert: (validMutabilities includes: tool mutability). + self assert: (true = tool annotations readOnlyHint or: [ false = tool annotations readOnlyHint ]). self assert: tool keywords notEmpty ] ] @@ -1344,10 +1343,12 @@ MCPToolContractsTest >> testDiscoveryUsesConfiguredStaticToolNames [ (server rpcToolCall: 'tool_get' withParams: { (#toolName -> 'repository_search') } asDictionary)) at: #tool. createContract := (self dataFrom: (server rpcToolCall: 'tool_get' withParams: { (#toolName -> 'repository_create') } asDictionary)) at: #tool. - self assert: searchContract keys asSet equals: #( name description group exposure mutability keywords inputSchema ) asSet. - self assert: createContract keys asSet equals: #( name description group exposure mutability keywords inputSchema ) asSet. + self assert: searchContract keys asSet equals: #( name description group exposure annotations keywords inputSchema ) asSet. + self assert: createContract keys asSet equals: #( name description group exposure annotations keywords inputSchema ) asSet. self assert: (searchContract at: #exposure) equals: 'static'. - self assert: (createContract at: #exposure) equals: 'discoverable' + self assert: ((searchContract at: #annotations) at: #readOnlyHint). + self assert: (createContract at: #exposure) equals: 'discoverable'. + self deny: ((createContract at: #annotations) at: #readOnlyHint) ] { #category : 'tests - evaluate' } @@ -1488,10 +1489,10 @@ MCPToolContractsTest >> testGetToolReturnsSchemaForCatalogTool [ result := self callToolNamed: 'tool_get' withArguments: { (#toolName -> 'repository_create') } asDictionary. contract := (self dataFrom: result) at: #tool. self assert: (contract at: #name) equals: 'repository_create'. - self assert: contract keys asSet equals: #( name description group exposure mutability keywords inputSchema ) asSet. + self assert: contract keys asSet equals: #( name description group exposure annotations keywords inputSchema ) asSet. self assert: (contract at: #group) equals: 'repositories'. self assert: (contract at: #exposure) equals: 'discoverable'. - self assert: (contract at: #mutability) equals: 'write'. + self deny: ((contract at: #annotations) at: #readOnlyHint). self assert: ((contract at: #keywords) includes: 'repo'). inputProperties := ((contract at: #inputSchema) at: #properties) keys. self assert: inputProperties asSet equals: #( 'name' 'location' 'packageNames' 'subdirectory' ) asSet. @@ -3209,6 +3210,25 @@ MCPToolContractsTest >> testStringArrayItemDescriptionsStayUseful [ equals: 'Smalltalk expression.' ] +{ #category : 'tests' } +MCPToolContractsTest >> testToolAnnotationsSerializeStandardHints [ + + | annotations | + annotations := MCPStructureToolAnnotations new + title: 'Safe Update'; + readOnlyHint: false; + destructiveHint: false; + idempotentHint: true; + openWorldHint: false; + yourself. + self assert: annotations asJRPCJSON equals: { + (#title -> 'Safe Update'). + (#readOnlyHint -> false). + (#destructiveHint -> false). + (#idempotentHint -> true). + (#openWorldHint -> false) } asDictionary +] + { #category : 'tests' } MCPToolContractsTest >> testToolFlowSelectorNamesSeparateTransportAndParsedInput [ @@ -3367,7 +3387,7 @@ MCPToolContractsTest >> testToolsOwnRegistryMetadata [ tool := MCPToolEvaluate new. self assert: tool groupName equals: 'runtime'. self assert: tool defaultExposure equals: 'static'. - self assert: tool mutability equals: 'execute'. + self deny: tool annotations readOnlyHint. self assert: (tool keywords includes: 'smalltalk') ] diff --git a/src/MCP-Tests/MCPToolMethodLookupTest.class.st b/src/MCP-Tests/MCPToolMethodLookupTest.class.st index 91331f0..af04231 100644 --- a/src/MCP-Tests/MCPToolMethodLookupTest.class.st +++ b/src/MCP-Tests/MCPToolMethodLookupTest.class.st @@ -65,14 +65,14 @@ MCPToolMethodLookupTest >> testLookupToolsArePublicStaticReadTools [ self assert: (self toolRegistry staticToolNames includes: each). tool := self toolRegistry toolNamed: each ifAbsent: [ self fail: 'Missing tool ' , each ]. self assert: tool defaultExposure equals: 'static'. - self assert: tool mutability equals: 'read' ]. + self assert: tool annotations readOnlyHint ]. discoverableNames do: [ :each | | tool | self assert: (self toolRegistry publicToolNames includes: each). self deny: (self toolRegistry staticToolNames includes: each). tool := self toolRegistry toolNamed: each ifAbsent: [ self fail: 'Missing tool ' , each ]. self assert: tool defaultExposure equals: 'discoverable'. - self assert: tool mutability equals: 'read' ] + self assert: tool annotations readOnlyHint ] ] { #category : 'tests' } diff --git a/src/MCP-Tests/MCPToolStructuredOutputTest.class.st b/src/MCP-Tests/MCPToolStructuredOutputTest.class.st index faf7b2e..182445e 100644 --- a/src/MCP-Tests/MCPToolStructuredOutputTest.class.st +++ b/src/MCP-Tests/MCPToolStructuredOutputTest.class.st @@ -654,5 +654,5 @@ MCPToolStructuredOutputTest >> testToolsListAdvertisesPublicToolContracts [ | tools | tools := MCP new rpcToolsList at: #tools. self assert: tools notEmpty. - tools do: [ :tool | self assert: tool keys asSet equals: #( name title description inputSchema ) asSet ] + tools do: [ :tool | self assert: tool keys asSet equals: #( name title description annotations inputSchema ) asSet ] ] diff --git a/src/MCP/MCPStructureToolAnnotations.class.st b/src/MCP/MCPStructureToolAnnotations.class.st new file mode 100644 index 0000000..9b9166c --- /dev/null +++ b/src/MCP/MCPStructureToolAnnotations.class.st @@ -0,0 +1,106 @@ +" +MCP protocol structure for optional tool behavior hints. Tool annotations are advisory metadata; clients must not treat them as trusted authorization decisions. +" +Class { + #name : 'MCPStructureToolAnnotations', + #superclass : 'Object', + #instVars : [ + 'readOnlyHint', + 'destructiveHint', + 'idempotentHint', + 'openWorldHint', + 'title' + ], + #category : 'MCP-Structure', + #package : 'MCP', + #tag : 'Structure' +} + +{ #category : 'instance creation' } +MCPStructureToolAnnotations class >> mayModify [ + + ^ self new + readOnlyHint: false; + yourself +] + +{ #category : 'instance creation' } +MCPStructureToolAnnotations class >> readOnly [ + + ^ self new + readOnlyHint: true; + yourself +] + +{ #category : 'converting' } +MCPStructureToolAnnotations >> asJRPCJSON [ + + | dictionary | + dictionary := Dictionary new. + self title ifNotNil: [ :annotationTitle | dictionary at: #title put: annotationTitle ]. + self readOnlyHint ifNotNil: [ :hint | dictionary at: #readOnlyHint put: hint ]. + self destructiveHint ifNotNil: [ :hint | dictionary at: #destructiveHint put: hint ]. + self idempotentHint ifNotNil: [ :hint | dictionary at: #idempotentHint put: hint ]. + self openWorldHint ifNotNil: [ :hint | dictionary at: #openWorldHint put: hint ]. + ^ dictionary +] + +{ #category : 'accessing' } +MCPStructureToolAnnotations >> destructiveHint [ + + ^ destructiveHint +] + +{ #category : 'accessing' } +MCPStructureToolAnnotations >> destructiveHint: aBoolean [ + + destructiveHint := aBoolean +] + +{ #category : 'accessing' } +MCPStructureToolAnnotations >> idempotentHint [ + + ^ idempotentHint +] + +{ #category : 'accessing' } +MCPStructureToolAnnotations >> idempotentHint: aBoolean [ + + idempotentHint := aBoolean +] + +{ #category : 'accessing' } +MCPStructureToolAnnotations >> openWorldHint [ + + ^ openWorldHint +] + +{ #category : 'accessing' } +MCPStructureToolAnnotations >> openWorldHint: aBoolean [ + + openWorldHint := aBoolean +] + +{ #category : 'accessing' } +MCPStructureToolAnnotations >> readOnlyHint [ + + ^ readOnlyHint +] + +{ #category : 'accessing' } +MCPStructureToolAnnotations >> readOnlyHint: aBoolean [ + + readOnlyHint := aBoolean +] + +{ #category : 'accessing' } +MCPStructureToolAnnotations >> title [ + + ^ title +] + +{ #category : 'accessing' } +MCPStructureToolAnnotations >> title: aString [ + + title := aString +] diff --git a/src/MCP/MCPTool.class.st b/src/MCP/MCPTool.class.st index 3a63ad2..886edcf 100644 --- a/src/MCP/MCPTool.class.st +++ b/src/MCP/MCPTool.class.st @@ -1,7 +1,7 @@ " Abstract base for MCP tools exposed by the server. -A tool owns its public MCP metadata: name, title, description, icons, input schema, and output schema. The server uses that metadata when advertising tools and when validating an incoming tool call. +A tool owns its public MCP metadata: name, title, description, icons, annotations, input schema, and output schema. The server uses that metadata when advertising tools and when validating an incoming tool call. The normal execution flow is: 1. The MCP server receives a JSON-RPC tool call and dispatches it to the matching MCPTool instance. @@ -82,29 +82,28 @@ MCPTool class >> toolName [ ] { #category : 'metadata' } -MCPTool >> activationRequirements [ +MCPTool >> additionalKeywords [ ^ #( ) ] { #category : 'metadata' } -MCPTool >> additionalKeywords [ +MCPTool >> annotations [ - ^ #( ) + ^ MCPStructureToolAnnotations mayModify ] { #category : 'accessing' } MCPTool >> asJRPCJSON [ - | dic | - dic := Dictionary new. - self description ifNotNil: [ - dic at: #description put: self description asJRPCJSON ]. - self inputSchema ifNotNil: [ - dic at: #inputSchema put: self inputSchema asJRPCJSON ]. - self name ifNotNil: [ dic at: #name put: self name asJRPCJSON ]. - self title ifNotNil: [ dic at: #title put: self title asJRPCJSON ]. - ^ dic + | dictionary | + dictionary := Dictionary new. + self annotations ifNotNil: [ :toolAnnotations | dictionary at: #annotations put: toolAnnotations asJRPCJSON ]. + self description ifNotNil: [ dictionary at: #description put: self description asJRPCJSON ]. + self inputSchema ifNotNil: [ dictionary at: #inputSchema put: self inputSchema asJRPCJSON ]. + self name ifNotNil: [ dictionary at: #name put: self name asJRPCJSON ]. + self title ifNotNil: [ dictionary at: #title put: self title asJRPCJSON ]. + ^ dictionary ] { #category : 'private - schema' } @@ -147,14 +146,10 @@ MCPTool >> commandForRequest: aToolRequest [ { #category : 'metadata' } MCPTool >> contractIncludingOutputSchema: includeOutputSchema [ - | activationRequirements data examples | + | data | data := self metadata copy. data at: #inputSchema put: self inputSchema asJRPCJSON. includeOutputSchema ifTrue: [ data at: #outputSchema put: self outputSchema asJRPCJSON ]. - examples := self examples. - examples ifNotEmpty: [ data at: #examples put: examples ]. - activationRequirements := self activationRequirements. - activationRequirements ifNotEmpty: [ data at: #activationRequirements put: activationRequirements ]. ^ data ] @@ -268,12 +263,6 @@ MCPTool >> errorStackTraceLimit [ ^ 20 ] -{ #category : 'metadata' } -MCPTool >> examples [ - - ^ #( ) -] - { #category : 'private - execution' } MCPTool >> executeParsedRequestFrom: request do: executionBlock onError: errorBlock [ "Parse the transport request inside the same exception boundary as execution. @@ -371,16 +360,10 @@ MCPTool >> metadata [ (#name -> self name). (#description -> self description). (#group -> self groupName). - (#mutability -> self mutability). + (#annotations -> self annotations asJRPCJSON). (#keywords -> self keywords) } asDictionary ] -{ #category : 'metadata' } -MCPTool >> mutability [ - - ^ 'write' -] - { #category : 'metadata' } MCPTool >> name [ diff --git a/src/MCP/MCPToolCallTool.class.st b/src/MCP/MCPToolCallTool.class.st index 2bc7e87..a0bd97e 100644 --- a/src/MCP/MCPToolCallTool.class.st +++ b/src/MCP/MCPToolCallTool.class.st @@ -98,12 +98,6 @@ MCPToolCallTool >> groupName [ ^ 'tool-catalog' ] -{ #category : 'metadata' } -MCPToolCallTool >> mutability [ - - ^ 'execute' -] - { #category : 'private - request' } MCPToolCallTool >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolCaptureScreenshot.class.st b/src/MCP/MCPToolCaptureScreenshot.class.st index f56942d..a11b627 100644 --- a/src/MCP/MCPToolCaptureScreenshot.class.st +++ b/src/MCP/MCPToolCaptureScreenshot.class.st @@ -23,6 +23,12 @@ MCPToolCaptureScreenshot >> additionalKeywords [ ^ #( 'screenshot' 'capture' 'window' 'display' 'world' 'ui' ) ] +{ #category : 'metadata' } +MCPToolCaptureScreenshot >> annotations [ + + ^ MCPStructureToolAnnotations readOnly +] + { #category : 'metadata' } MCPToolCaptureScreenshot >> buildInputSchema [ @@ -102,12 +108,6 @@ MCPToolCaptureScreenshot >> groupName [ ^ 'ui' ] -{ #category : 'metadata' } -MCPToolCaptureScreenshot >> mutability [ - - ^ 'read' -] - { #category : 'private - request' } MCPToolCaptureScreenshot >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolDebugCapture.class.st b/src/MCP/MCPToolDebugCapture.class.st index 576bab3..eda0ea0 100644 --- a/src/MCP/MCPToolDebugCapture.class.st +++ b/src/MCP/MCPToolDebugCapture.class.st @@ -139,12 +139,6 @@ MCPToolDebugCapture >> description [ ^ 'Evaluate Smalltalk in a bounded worker process and capture runtime exceptions as tracked debug sessions.' ] -{ #category : 'metadata' } -MCPToolDebugCapture >> mutability [ - - ^ 'execute' -] - { #category : 'private - request' } MCPToolDebugCapture >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolDebugEvaluate.class.st b/src/MCP/MCPToolDebugEvaluate.class.st index 681f758..d41c4ce 100644 --- a/src/MCP/MCPToolDebugEvaluate.class.st +++ b/src/MCP/MCPToolDebugEvaluate.class.st @@ -122,12 +122,6 @@ MCPToolDebugEvaluate >> description [ ^ 'Evaluate a Smalltalk expression in a selected DebugSession frame. The expression resolves frame receiver and temporaries and may mutate image state.' ] -{ #category : 'metadata' } -MCPToolDebugEvaluate >> mutability [ - - ^ 'execute' -] - { #category : 'private - request' } MCPToolDebugEvaluate >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolDebugState.class.st b/src/MCP/MCPToolDebugState.class.st index 95dc95b..8284753 100644 --- a/src/MCP/MCPToolDebugState.class.st +++ b/src/MCP/MCPToolDebugState.class.st @@ -17,6 +17,12 @@ MCPToolDebugState class >> toolName [ ^ 'debug_state_get' ] +{ #category : 'metadata' } +MCPToolDebugState >> annotations [ + + ^ MCPStructureToolAnnotations readOnly +] + { #category : 'metadata' } MCPToolDebugState >> buildInputSchema [ @@ -151,12 +157,6 @@ MCPToolDebugState >> description [ ^ 'Return a compact snapshot of one tracked Pharo debug session: selected frame, source, stack, shallow scopes, and repair actions.' ] -{ #category : 'metadata' } -MCPToolDebugState >> mutability [ - - ^ 'read' -] - { #category : 'private - request' } MCPToolDebugState >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolDebugTest.class.st b/src/MCP/MCPToolDebugTest.class.st index f6e57c4..87dd483 100644 --- a/src/MCP/MCPToolDebugTest.class.st +++ b/src/MCP/MCPToolDebugTest.class.st @@ -154,12 +154,6 @@ MCPToolDebugTest >> description [ ^ 'Run one SUnit test method under debugger control and stop at the requested phase.' ] -{ #category : 'metadata' } -MCPToolDebugTest >> mutability [ - - ^ 'execute' -] - { #category : 'private - request' } MCPToolDebugTest >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolDebugVariables.class.st b/src/MCP/MCPToolDebugVariables.class.st index 52233d0..cda9355 100644 --- a/src/MCP/MCPToolDebugVariables.class.st +++ b/src/MCP/MCPToolDebugVariables.class.st @@ -17,6 +17,12 @@ MCPToolDebugVariables class >> toolName [ ^ 'debug_variable_get' ] +{ #category : 'metadata' } +MCPToolDebugVariables >> annotations [ + + ^ MCPStructureToolAnnotations readOnly +] + { #category : 'metadata' } MCPToolDebugVariables >> buildInputSchema [ @@ -127,12 +133,6 @@ MCPToolDebugVariables >> description [ ^ 'Expand one state-scoped variable or scope reference returned by debug_state_get.' ] -{ #category : 'metadata' } -MCPToolDebugVariables >> mutability [ - - ^ 'read' -] - { #category : 'private - request' } MCPToolDebugVariables >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolEvaluate.class.st b/src/MCP/MCPToolEvaluate.class.st index 811e4cd..88c1dd5 100644 --- a/src/MCP/MCPToolEvaluate.class.st +++ b/src/MCP/MCPToolEvaluate.class.st @@ -108,12 +108,6 @@ MCPToolEvaluate >> groupName [ ^ 'runtime' ] -{ #category : 'metadata' } -MCPToolEvaluate >> mutability [ - - ^ 'execute' -] - { #category : 'private - request' } MCPToolEvaluate >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolGetClass.class.st b/src/MCP/MCPToolGetClass.class.st index 973e9f0..de569b6 100644 --- a/src/MCP/MCPToolGetClass.class.st +++ b/src/MCP/MCPToolGetClass.class.st @@ -17,6 +17,12 @@ MCPToolGetClass class >> toolName [ ^ 'class_get' ] +{ #category : 'metadata' } +MCPToolGetClass >> annotations [ + + ^ MCPStructureToolAnnotations readOnly +] + { #category : 'metadata' } MCPToolGetClass >> buildInputSchema [ @@ -294,12 +300,6 @@ MCPToolGetClass >> maximumSubclassDepth [ ^ 3 ] -{ #category : 'metadata' } -MCPToolGetClass >> mutability [ - - ^ 'read' -] - { #category : 'private - request' } MCPToolGetClass >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolGetDebugSession.class.st b/src/MCP/MCPToolGetDebugSession.class.st index 8aee219..e6185f9 100644 --- a/src/MCP/MCPToolGetDebugSession.class.st +++ b/src/MCP/MCPToolGetDebugSession.class.st @@ -16,9 +16,9 @@ MCPToolGetDebugSession class >> toolName [ ] { #category : 'metadata' } -MCPToolGetDebugSession >> mutability [ +MCPToolGetDebugSession >> annotations [ - ^ 'read' + ^ MCPStructureToolAnnotations readOnly ] { #category : 'private - specs' } diff --git a/src/MCP/MCPToolGetMethod.class.st b/src/MCP/MCPToolGetMethod.class.st index 6644d1b..fa04afb 100644 --- a/src/MCP/MCPToolGetMethod.class.st +++ b/src/MCP/MCPToolGetMethod.class.st @@ -17,6 +17,12 @@ MCPToolGetMethod class >> toolName [ ^ 'method_get' ] +{ #category : 'metadata' } +MCPToolGetMethod >> annotations [ + + ^ MCPStructureToolAnnotations readOnly +] + { #category : 'private - analysis' } MCPToolGetMethod >> bindingClassNameFor: aBinding [ @@ -220,12 +226,6 @@ MCPToolGetMethod >> matchingSummaryForNodeName: aName kind: aKind ownerName: anO ifNone: [ nil ] ] -{ #category : 'metadata' } -MCPToolGetMethod >> mutability [ - - ^ 'read' -] - { #category : 'private - analysis' } MCPToolGetMethod >> newVariableSummaryForNode: aVariableNode kind: aKind methodBehavior: aBehavior [ diff --git a/src/MCP/MCPToolGetTool.class.st b/src/MCP/MCPToolGetTool.class.st index d77e0c5..aa0906a 100644 --- a/src/MCP/MCPToolGetTool.class.st +++ b/src/MCP/MCPToolGetTool.class.st @@ -28,6 +28,35 @@ MCPToolGetTool class >> toolName [ ^ 'tool_get' ] +{ #category : 'metadata' } +MCPToolGetTool >> annotations [ + + ^ MCPStructureToolAnnotations readOnly +] + +{ #category : 'private - schema' } +MCPToolGetTool >> annotationsSchemaProperty [ + + | property | + property := self schemaPropertyNamed: 'annotations' type: 'object' description: 'Standard MCP tool behavior hints.'. + property + properties: { + (self schemaPropertyNamed: 'title' type: 'string' description: 'Optional annotation title.'). + (self schemaPropertyNamed: 'readOnlyHint' type: 'boolean' description: 'Whether the tool leaves its environment unchanged.'). + (self + schemaPropertyNamed: 'destructiveHint' + type: 'boolean' + description: 'Whether a modifying tool may perform destructive updates.'). + (self schemaPropertyNamed: 'idempotentHint' type: 'boolean' description: 'Whether repeated calls have no additional effect.'). + (self + schemaPropertyNamed: 'openWorldHint' + type: 'boolean' + description: 'Whether the tool may interact with external entities.') }; + required: #( 'readOnlyHint' ); + additionalProperties: false. + ^ property +] + { #category : 'metadata' } MCPToolGetTool >> buildInputSchema [ @@ -45,58 +74,26 @@ MCPToolGetTool >> buildInputSchema [ { #category : 'metadata' } MCPToolGetTool >> buildOutputSchema [ - | activationRequirementsProperty examplesProperty inputSchemaProperty outputSchemaProperty schemaProperty toolProperty | - schemaProperty := MCPStructureProperties new - type: 'object'; - description: 'JSON Schema object.'; - additionalProperties: true; - yourself. - inputSchemaProperty := schemaProperty copy - name: 'inputSchema'; - yourself. - outputSchemaProperty := schemaProperty copy - name: 'outputSchema'; - yourself. - examplesProperty := (self - schemaPropertyNamed: 'examples' - type: 'array' - description: 'Example calls.') - items: (MCPStructureProperties new - type: 'object'; - additionalProperties: true; - yourself); - yourself. - activationRequirementsProperty := (self - schemaPropertyNamed: 'activationRequirements' - type: 'array' - description: 'Requirements before calling tool.') - items: (MCPStructureProperties new - type: 'string'; - yourself); - yourself. - toolProperty := self - schemaPropertyNamed: 'tool' - type: 'object' - description: 'Tool contract.'. + | inputSchemaProperty toolProperty | + inputSchemaProperty := MCPStructureProperties new + name: 'inputSchema'; + type: 'object'; + description: 'JSON Schema object.'; + additionalProperties: true; + yourself. + toolProperty := self schemaPropertyNamed: 'tool' type: 'object' description: 'Tool contract.'. toolProperty properties: { - (self - schemaPropertyNamed: 'name' - type: 'string' - description: 'Tool name.'). - (self - schemaPropertyNamed: 'description' - type: 'string' - description: 'Tool description.'). - inputSchemaProperty. - outputSchemaProperty. - examplesProperty. - activationRequirementsProperty }; - required: #( 'name' 'description' 'inputSchema' ); + (self schemaPropertyNamed: 'name' type: 'string' description: 'Tool name.'). + (self schemaPropertyNamed: 'description' type: 'string' description: 'Tool description.'). + (self schemaPropertyNamed: 'group' type: 'string' description: 'Tool catalog group.'). + (self schemaPropertyNamed: 'exposure' type: 'string' description: 'Effective tool exposure.'). + self annotationsSchemaProperty. + self keywordsSchemaProperty. + inputSchemaProperty }; + required: #( 'name' 'description' 'group' 'exposure' 'annotations' 'keywords' 'inputSchema' ); additionalProperties: false. - ^ self - standardOutputSchemaForDataProperties: { toolProperty } - required: #( 'tool' ) + ^ self standardOutputSchemaForDataProperties: { toolProperty } required: #( 'tool' ) ] { #category : 'private - execution' } @@ -136,10 +133,14 @@ MCPToolGetTool >> groupName [ ^ 'tool-catalog' ] -{ #category : 'metadata' } -MCPToolGetTool >> mutability [ +{ #category : 'private - schema' } +MCPToolGetTool >> keywordsSchemaProperty [ - ^ 'read' + ^ (self schemaPropertyNamed: 'keywords' type: 'array' description: 'Search terms associated with the tool.') + items: (MCPStructureProperties new + type: 'string'; + yourself); + yourself ] { #category : 'private - request' } diff --git a/src/MCP/MCPToolListChangeHistoryEntries.class.st b/src/MCP/MCPToolListChangeHistoryEntries.class.st index 8247ed5..56f9cd3 100644 --- a/src/MCP/MCPToolListChangeHistoryEntries.class.st +++ b/src/MCP/MCPToolListChangeHistoryEntries.class.st @@ -15,6 +15,12 @@ MCPToolListChangeHistoryEntries class >> toolName [ ^ 'history_entry_list' ] +{ #category : 'metadata' } +MCPToolListChangeHistoryEntries >> annotations [ + + ^ MCPStructureToolAnnotations readOnly +] + { #category : 'metadata' } MCPToolListChangeHistoryEntries >> buildInputSchema [ @@ -91,12 +97,6 @@ MCPToolListChangeHistoryEntries >> description [ ^ 'List granular entries from the current Pharo image change history or a selected .ombu file. Use history_file_list to locate files, and history_entry_apply or history_entry_revert only when previewing or performing recovery changes.' ] -{ #category : 'metadata' } -MCPToolListChangeHistoryEntries >> mutability [ - - ^ 'read' -] - { #category : 'private - request' } MCPToolListChangeHistoryEntries >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolListChangeHistoryFiles.class.st b/src/MCP/MCPToolListChangeHistoryFiles.class.st index 73a6c79..b4dc376 100644 --- a/src/MCP/MCPToolListChangeHistoryFiles.class.st +++ b/src/MCP/MCPToolListChangeHistoryFiles.class.st @@ -15,6 +15,12 @@ MCPToolListChangeHistoryFiles class >> toolName [ ^ 'history_file_list' ] +{ #category : 'metadata' } +MCPToolListChangeHistoryFiles >> annotations [ + + ^ MCPStructureToolAnnotations readOnly +] + { #category : 'metadata' } MCPToolListChangeHistoryFiles >> buildInputSchema [ @@ -86,12 +92,6 @@ MCPToolListChangeHistoryFiles >> maximumFileLimit [ ^ 100 ] -{ #category : 'metadata' } -MCPToolListChangeHistoryFiles >> mutability [ - - ^ 'read' -] - { #category : 'private - request' } MCPToolListChangeHistoryFiles >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolListDebugBreakpoints.class.st b/src/MCP/MCPToolListDebugBreakpoints.class.st index 77cfe95..31f62ef 100644 --- a/src/MCP/MCPToolListDebugBreakpoints.class.st +++ b/src/MCP/MCPToolListDebugBreakpoints.class.st @@ -15,6 +15,12 @@ MCPToolListDebugBreakpoints class >> toolName [ ^ 'debug_breakpoint_list' ] +{ #category : 'metadata' } +MCPToolListDebugBreakpoints >> annotations [ + + ^ MCPStructureToolAnnotations readOnly +] + { #category : 'private - specs' } MCPToolListDebugBreakpoints >> breakpointToolSpec [ @@ -24,9 +30,3 @@ MCPToolListDebugBreakpoints >> breakpointToolSpec [ -> 'List tracked transient Pharo DebugPoint breakpoints.'). (#action -> 'list') } asDictionary ] - -{ #category : 'metadata' } -MCPToolListDebugBreakpoints >> mutability [ - - ^ 'read' -] diff --git a/src/MCP/MCPToolListDebugSessions.class.st b/src/MCP/MCPToolListDebugSessions.class.st index b1ee19c..1b5979a 100644 --- a/src/MCP/MCPToolListDebugSessions.class.st +++ b/src/MCP/MCPToolListDebugSessions.class.st @@ -16,9 +16,9 @@ MCPToolListDebugSessions class >> toolName [ ] { #category : 'metadata' } -MCPToolListDebugSessions >> mutability [ +MCPToolListDebugSessions >> annotations [ - ^ 'read' + ^ MCPStructureToolAnnotations readOnly ] { #category : 'private - specs' } diff --git a/src/MCP/MCPToolListRepositoryChanges.class.st b/src/MCP/MCPToolListRepositoryChanges.class.st index 45eb16b..5df3b44 100644 --- a/src/MCP/MCPToolListRepositoryChanges.class.st +++ b/src/MCP/MCPToolListRepositoryChanges.class.st @@ -22,9 +22,9 @@ MCPToolListRepositoryChanges >> additionalKeywords [ ] { #category : 'metadata' } -MCPToolListRepositoryChanges >> mutability [ +MCPToolListRepositoryChanges >> annotations [ - ^ 'read' + ^ MCPStructureToolAnnotations readOnly ] { #category : 'private - specs' } diff --git a/src/MCP/MCPToolRunTests.class.st b/src/MCP/MCPToolRunTests.class.st index b9e786f..c765268 100644 --- a/src/MCP/MCPToolRunTests.class.st +++ b/src/MCP/MCPToolRunTests.class.st @@ -295,12 +295,6 @@ MCPToolRunTests >> groupName [ ^ 'tests' ] -{ #category : 'metadata' } -MCPToolRunTests >> mutability [ - - ^ 'execute' -] - { #category : 'private - request' } MCPToolRunTests >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolSearch.class.st b/src/MCP/MCPToolSearch.class.st index 0a3220b..f40a989 100644 --- a/src/MCP/MCPToolSearch.class.st +++ b/src/MCP/MCPToolSearch.class.st @@ -21,6 +21,12 @@ MCPToolSearch >> addScopeLabel: aString to: labels [ (labels includes: aString) ifFalse: [ labels add: aString ] ] +{ #category : 'metadata' } +MCPToolSearch >> annotations [ + + ^ MCPStructureToolAnnotations readOnly +] + { #category : 'private - schema' } MCPToolSearch >> basicFilterModes [ @@ -349,12 +355,6 @@ MCPToolSearch >> minimalPagedQueryOutputSchemaForEntriesPropertyNamed: entriesPr required: { entriesPropertyName } ] -{ #category : 'metadata' } -MCPToolSearch >> mutability [ - - ^ 'read' -] - { #category : 'private - filtering' } MCPToolSearch >> normalizeFilterString: aValue caseSensitive: aBoolean [ diff --git a/src/MCP/MCPToolSearchDebugCandidates.class.st b/src/MCP/MCPToolSearchDebugCandidates.class.st index 3850edb..fd83fff 100644 --- a/src/MCP/MCPToolSearchDebugCandidates.class.st +++ b/src/MCP/MCPToolSearchDebugCandidates.class.st @@ -16,9 +16,9 @@ MCPToolSearchDebugCandidates class >> toolName [ ] { #category : 'metadata' } -MCPToolSearchDebugCandidates >> mutability [ +MCPToolSearchDebugCandidates >> annotations [ - ^ 'read' + ^ MCPStructureToolAnnotations readOnly ] { #category : 'private - specs' } diff --git a/src/MCP/MCPToolSearchTools.class.st b/src/MCP/MCPToolSearchTools.class.st index 824446b..7d7cb50 100644 --- a/src/MCP/MCPToolSearchTools.class.st +++ b/src/MCP/MCPToolSearchTools.class.st @@ -28,6 +28,12 @@ MCPToolSearchTools class >> toolName [ ^ 'tool_search' ] +{ #category : 'metadata' } +MCPToolSearchTools >> annotations [ + + ^ MCPStructureToolAnnotations readOnly +] + { #category : 'metadata' } MCPToolSearchTools >> buildInputSchema [ diff --git a/src/MCP/MCPToolVerifyRepositoryIdentity.class.st b/src/MCP/MCPToolVerifyRepositoryIdentity.class.st index 7ccaa5e..5b25ce3 100644 --- a/src/MCP/MCPToolVerifyRepositoryIdentity.class.st +++ b/src/MCP/MCPToolVerifyRepositoryIdentity.class.st @@ -22,9 +22,9 @@ MCPToolVerifyRepositoryIdentity >> additionalKeywords [ ] { #category : 'metadata' } -MCPToolVerifyRepositoryIdentity >> mutability [ +MCPToolVerifyRepositoryIdentity >> annotations [ - ^ 'read' + ^ MCPStructureToolAnnotations readOnly ] { #category : 'private - specs' } From 9adf72c9b7362f57f7104aa432b90428d0709a8b Mon Sep 17 00:00:00 2001 From: Gabriel Darbord <78592838+Gabriel-Darbord@users.noreply.github.com> Date: Mon, 20 Jul 2026 11:20:02 +0200 Subject: [PATCH 5/5] Centralize built-in tool group names --- src/MCP-Tests/MCPToolContractsTest.class.st | 3 +- src/MCP/MCPTool.class.st | 90 ++++++++++++++++++- src/MCP/MCPToolCallTool.class.st | 12 +-- src/MCP/MCPToolCaptureScreenshot.class.st | 12 +-- src/MCP/MCPToolChangeHistory.class.st | 12 +-- src/MCP/MCPToolClassMutation.class.st | 12 +-- src/MCP/MCPToolDebug.class.st | 12 +-- src/MCP/MCPToolEvaluate.class.st | 12 +-- src/MCP/MCPToolGetClass.class.st | 12 +-- src/MCP/MCPToolGetMethod.class.st | 12 +-- src/MCP/MCPToolGetTool.class.st | 12 +-- src/MCP/MCPToolLoadMetacello.class.st | 12 +-- src/MCP/MCPToolMethodLookupOperation.class.st | 12 +-- src/MCP/MCPToolMethodMutation.class.st | 12 +-- src/MCP/MCPToolMethodSearch.class.st | 12 +-- src/MCP/MCPToolRemoveClasses.class.st | 12 +-- src/MCP/MCPToolRemoveMethods.class.st | 12 +-- src/MCP/MCPToolRepositoryOperation.class.st | 12 +-- src/MCP/MCPToolRewriteMethods.class.st | 12 +-- src/MCP/MCPToolRunTests.class.st | 12 +-- src/MCP/MCPToolSearchClasses.class.st | 12 +-- src/MCP/MCPToolSearchPackages.class.st | 12 +-- src/MCP/MCPToolSearchRepositories.class.st | 12 +-- src/MCP/MCPToolSearchTools.class.st | 12 +-- 24 files changed, 223 insertions(+), 134 deletions(-) diff --git a/src/MCP-Tests/MCPToolContractsTest.class.st b/src/MCP-Tests/MCPToolContractsTest.class.st index 8cf685d..a19143d 100644 --- a/src/MCP-Tests/MCPToolContractsTest.class.st +++ b/src/MCP-Tests/MCPToolContractsTest.class.st @@ -902,7 +902,8 @@ MCPToolContractsTest >> testBuiltInToolsProvideCatalogMetadata [ | validExposures | validExposures := #( 'static' 'discoverable' ). self toolRegistry publicTools do: [ :tool | - self deny: tool groupName equals: 'other'. + self assert: (MCPTool builtInGroupNames includes: tool groupName). + self assert: tool groupName equals: tool class groupName. self assert: (validExposures includes: tool defaultExposure). self assert: (true = tool annotations readOnlyHint or: [ false = tool annotations readOnlyHint ]). self assert: tool keywords notEmpty ] diff --git a/src/MCP/MCPTool.class.st b/src/MCP/MCPTool.class.st index 886edcf..1177503 100644 --- a/src/MCP/MCPTool.class.st +++ b/src/MCP/MCPTool.class.st @@ -26,12 +26,52 @@ Class { #tag : 'Tools' } +{ #category : 'metadata - groups' } +MCPTool class >> builtInGroupNames [ + + ^ { + self classesGroupName. + self debuggingGroupName. + self historyGroupName. + self methodsGroupName. + self packagesGroupName. + self repositoriesGroupName. + self runtimeGroupName. + self testsGroupName. + self toolCatalogGroupName. + self uiGroupName } +] + +{ #category : 'metadata - groups' } +MCPTool class >> classesGroupName [ + + ^ 'classes' +] + { #category : 'querying' } MCPTool class >> concreteSubclasses [ ^ self allSubclasses reject: [ :each | each isAbstract ] ] +{ #category : 'metadata - groups' } +MCPTool class >> debuggingGroupName [ + + ^ 'debugging' +] + +{ #category : 'metadata' } +MCPTool class >> groupName [ + + ^ self otherGroupName +] + +{ #category : 'metadata - groups' } +MCPTool class >> historyGroupName [ + + ^ 'history' +] + { #category : 'testing' } MCPTool class >> isAbstract [ @@ -44,12 +84,54 @@ MCPTool class >> isBuiltIn [ ^ self package = MCPTool package ] +{ #category : 'metadata - groups' } +MCPTool class >> methodsGroupName [ + + ^ 'methods' +] + { #category : 'instance creation' } MCPTool class >> newForRegistry: aToolRegistry [ ^ self new ] +{ #category : 'metadata - groups' } +MCPTool class >> otherGroupName [ + + ^ 'other' +] + +{ #category : 'metadata - groups' } +MCPTool class >> packagesGroupName [ + + ^ 'packages' +] + +{ #category : 'metadata - groups' } +MCPTool class >> repositoriesGroupName [ + + ^ 'repositories' +] + +{ #category : 'metadata - groups' } +MCPTool class >> runtimeGroupName [ + + ^ 'runtime' +] + +{ #category : 'metadata - groups' } +MCPTool class >> testsGroupName [ + + ^ 'tests' +] + +{ #category : 'metadata - groups' } +MCPTool class >> toolCatalogGroupName [ + + ^ 'tool-catalog' +] + { #category : 'scripts' } MCPTool class >> toolGroups [ @@ -81,6 +163,12 @@ MCPTool class >> toolName [ self subclassResponsibility ] +{ #category : 'metadata - groups' } +MCPTool class >> uiGroupName [ + + ^ 'ui' +] + { #category : 'metadata' } MCPTool >> additionalKeywords [ @@ -305,7 +393,7 @@ MCPTool >> failureMessageFor: anError [ { #category : 'metadata' } MCPTool >> groupName [ - ^ 'other' + ^ self class groupName ] { #category : 'metadata' } diff --git a/src/MCP/MCPToolCallTool.class.st b/src/MCP/MCPToolCallTool.class.st index a0bd97e..9a10ae4 100644 --- a/src/MCP/MCPToolCallTool.class.st +++ b/src/MCP/MCPToolCallTool.class.st @@ -11,6 +11,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolCallTool class >> groupName [ + + ^ self toolCatalogGroupName +] + { #category : 'metadata' } MCPToolCallTool class >> toolName [ @@ -92,12 +98,6 @@ MCPToolCallTool >> executeWithRequest: request [ onError: [ :error :parsedRequest | self errorResultFor: error ] ] -{ #category : 'metadata' } -MCPToolCallTool >> groupName [ - - ^ 'tool-catalog' -] - { #category : 'private - request' } MCPToolCallTool >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolCaptureScreenshot.class.st b/src/MCP/MCPToolCaptureScreenshot.class.st index a11b627..fc6411f 100644 --- a/src/MCP/MCPToolCaptureScreenshot.class.st +++ b/src/MCP/MCPToolCaptureScreenshot.class.st @@ -11,6 +11,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolCaptureScreenshot class >> groupName [ + + ^ self uiGroupName +] + { #category : 'metadata' } MCPToolCaptureScreenshot class >> toolName [ @@ -102,12 +108,6 @@ MCPToolCaptureScreenshot >> executeWithRequest: request [ onError: [ :error :ignored | self errorResultText: (self failureMessageFor: error) details: (self errorDetailsFor: error) ] ] -{ #category : 'metadata' } -MCPToolCaptureScreenshot >> groupName [ - - ^ 'ui' -] - { #category : 'private - request' } MCPToolCaptureScreenshot >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolChangeHistory.class.st b/src/MCP/MCPToolChangeHistory.class.st index b156062..ef673f1 100644 --- a/src/MCP/MCPToolChangeHistory.class.st +++ b/src/MCP/MCPToolChangeHistory.class.st @@ -11,6 +11,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolChangeHistory class >> groupName [ + + ^ self historyGroupName +] + { #category : 'testing' } MCPToolChangeHistory class >> isAbstract [ @@ -170,12 +176,6 @@ MCPToolChangeHistory >> executeWithRequest: request [ otherwise: [ :toolError | self errorResultFor: toolError ] ] ] -{ #category : 'metadata' } -MCPToolChangeHistory >> groupName [ - - ^ 'history' -] - { #category : 'private - schema' } MCPToolChangeHistory >> integerArraySchemaNamed: aName description: aDescription itemDescription: anItemDescription [ diff --git a/src/MCP/MCPToolClassMutation.class.st b/src/MCP/MCPToolClassMutation.class.st index 212b263..a28e9f9 100644 --- a/src/MCP/MCPToolClassMutation.class.st +++ b/src/MCP/MCPToolClassMutation.class.st @@ -13,6 +13,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolClassMutation class >> groupName [ + + ^ self classesGroupName +] + { #category : 'testing' } MCPToolClassMutation class >> isAbstract [ @@ -298,12 +304,6 @@ MCPToolClassMutation >> fixedArguments [ ^ self classToolSpec at: #fixedArguments ifAbsent: [ Dictionary new ] ] -{ #category : 'metadata' } -MCPToolClassMutation >> groupName [ - - ^ 'classes' -] - { #category : 'private - schema' } MCPToolClassMutation >> inputProperties [ diff --git a/src/MCP/MCPToolDebug.class.st b/src/MCP/MCPToolDebug.class.st index 4c21b01..81c5e1a 100644 --- a/src/MCP/MCPToolDebug.class.st +++ b/src/MCP/MCPToolDebug.class.st @@ -11,6 +11,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolDebug class >> groupName [ + + ^ self debuggingGroupName +] + { #category : 'testing' } MCPToolDebug class >> isAbstract [ @@ -82,12 +88,6 @@ MCPToolDebug >> executeWithRequest: request [ onError: [ :error :ignored | self errorResultForDebugError: error ] ] -{ #category : 'metadata' } -MCPToolDebug >> groupName [ - - ^ 'debugging' -] - { #category : 'private - schema' } MCPToolDebug >> inputProperties [ diff --git a/src/MCP/MCPToolEvaluate.class.st b/src/MCP/MCPToolEvaluate.class.st index 88c1dd5..0fa256a 100644 --- a/src/MCP/MCPToolEvaluate.class.st +++ b/src/MCP/MCPToolEvaluate.class.st @@ -9,6 +9,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolEvaluate class >> groupName [ + + ^ self runtimeGroupName +] + { #category : 'metadata' } MCPToolEvaluate class >> toolName [ @@ -102,12 +108,6 @@ MCPToolEvaluate >> executeWithRequest: request [ details: (self errorDetailsFor: error) ] ] -{ #category : 'metadata' } -MCPToolEvaluate >> groupName [ - - ^ 'runtime' -] - { #category : 'private - request' } MCPToolEvaluate >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolGetClass.class.st b/src/MCP/MCPToolGetClass.class.st index de569b6..a1dc38d 100644 --- a/src/MCP/MCPToolGetClass.class.st +++ b/src/MCP/MCPToolGetClass.class.st @@ -11,6 +11,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolGetClass class >> groupName [ + + ^ self classesGroupName +] + { #category : 'metadata' } MCPToolGetClass class >> toolName [ @@ -288,12 +294,6 @@ MCPToolGetClass >> failureMessageForClassName: className error: anError [ nextPutAll: anError messageText ] ] -{ #category : 'metadata' } -MCPToolGetClass >> groupName [ - - ^ 'classes' -] - { #category : 'defaults' } MCPToolGetClass >> maximumSubclassDepth [ diff --git a/src/MCP/MCPToolGetMethod.class.st b/src/MCP/MCPToolGetMethod.class.st index fa04afb..05e0036 100644 --- a/src/MCP/MCPToolGetMethod.class.st +++ b/src/MCP/MCPToolGetMethod.class.st @@ -11,6 +11,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolGetMethod class >> groupName [ + + ^ self methodsGroupName +] + { #category : 'metadata' } MCPToolGetMethod class >> toolName [ @@ -203,12 +209,6 @@ MCPToolGetMethod >> failureMessageForClassNamed: className selector: selectorStr nextPutAll: anError messageText ] ] -{ #category : 'metadata' } -MCPToolGetMethod >> groupName [ - - ^ 'methods' -] - { #category : 'private - analysis' } MCPToolGetMethod >> incrementReferenceCountInSummary: aSummary [ diff --git a/src/MCP/MCPToolGetTool.class.st b/src/MCP/MCPToolGetTool.class.st index aa0906a..0310ad7 100644 --- a/src/MCP/MCPToolGetTool.class.st +++ b/src/MCP/MCPToolGetTool.class.st @@ -14,6 +14,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolGetTool class >> groupName [ + + ^ self toolCatalogGroupName +] + { #category : 'instance creation' } MCPToolGetTool class >> newForRegistry: aToolRegistry [ @@ -127,12 +133,6 @@ MCPToolGetTool >> executeWithRequest: request [ onError: [ :error :parsedRequest | self errorResultFor: error ] ] -{ #category : 'metadata' } -MCPToolGetTool >> groupName [ - - ^ 'tool-catalog' -] - { #category : 'private - schema' } MCPToolGetTool >> keywordsSchemaProperty [ diff --git a/src/MCP/MCPToolLoadMetacello.class.st b/src/MCP/MCPToolLoadMetacello.class.st index 7eda329..9b82090 100644 --- a/src/MCP/MCPToolLoadMetacello.class.st +++ b/src/MCP/MCPToolLoadMetacello.class.st @@ -9,6 +9,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolLoadMetacello class >> groupName [ + + ^ self repositoriesGroupName +] + { #category : 'testing' } MCPToolLoadMetacello class >> isAbstract [ @@ -125,12 +131,6 @@ MCPToolLoadMetacello >> failureSummaryForError: anError loadRequest: loadRequest nextPutAll: message ] ] -{ #category : 'metadata' } -MCPToolLoadMetacello >> groupName [ - - ^ 'repositories' -] - { #category : 'private - schema' } MCPToolLoadMetacello >> groupsSchemaProperty [ diff --git a/src/MCP/MCPToolMethodLookupOperation.class.st b/src/MCP/MCPToolMethodLookupOperation.class.st index dbec36f..6f4cd6d 100644 --- a/src/MCP/MCPToolMethodLookupOperation.class.st +++ b/src/MCP/MCPToolMethodLookupOperation.class.st @@ -9,6 +9,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolMethodLookupOperation class >> groupName [ + + ^ self methodsGroupName +] + { #category : 'testing' } MCPToolMethodLookupOperation class >> isAbstract [ @@ -39,12 +45,6 @@ MCPToolMethodLookupOperation >> filterPatternFromRequest: request [ :value | value trimBoth ] ] -{ #category : 'metadata' } -MCPToolMethodLookupOperation >> groupName [ - - ^ 'methods' -] - { #category : 'private - schema' } MCPToolMethodLookupOperation >> lookupArgumentDescription [ diff --git a/src/MCP/MCPToolMethodMutation.class.st b/src/MCP/MCPToolMethodMutation.class.st index 372cea0..a7c1729 100644 --- a/src/MCP/MCPToolMethodMutation.class.st +++ b/src/MCP/MCPToolMethodMutation.class.st @@ -13,6 +13,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolMethodMutation class >> groupName [ + + ^ self methodsGroupName +] + { #category : 'testing' } MCPToolMethodMutation class >> isAbstract [ @@ -226,12 +232,6 @@ MCPToolMethodMutation >> failureMessageForRenameClassNamed: className classSide: (self normalizedCauseMessageForMutationError: anError) ] ] -{ #category : 'metadata' } -MCPToolMethodMutation >> groupName [ - - ^ 'methods' -] - { #category : 'private - schema' } MCPToolMethodMutation >> inputProperties [ diff --git a/src/MCP/MCPToolMethodSearch.class.st b/src/MCP/MCPToolMethodSearch.class.st index 785c232..bef84da 100644 --- a/src/MCP/MCPToolMethodSearch.class.st +++ b/src/MCP/MCPToolMethodSearch.class.st @@ -12,6 +12,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolMethodSearch class >> groupName [ + + ^ self methodsGroupName +] + { #category : 'testing' } MCPToolMethodSearch class >> isAbstract [ @@ -91,12 +97,6 @@ MCPToolMethodSearch >> filterTextsForMethodEntry: anEntry sourceText: sourceText ^ #( ) ] -{ #category : 'metadata' } -MCPToolMethodSearch >> groupName [ - - ^ 'methods' -] - { #category : 'private - methods' } MCPToolMethodSearch >> implementorEntriesForQueryRequest: queryRequest [ diff --git a/src/MCP/MCPToolRemoveClasses.class.st b/src/MCP/MCPToolRemoveClasses.class.st index d91cdfe..7bf8372 100644 --- a/src/MCP/MCPToolRemoveClasses.class.st +++ b/src/MCP/MCPToolRemoveClasses.class.st @@ -11,6 +11,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolRemoveClasses class >> groupName [ + + ^ self classesGroupName +] + { #category : 'metadata' } MCPToolRemoveClasses class >> toolName [ @@ -144,12 +150,6 @@ MCPToolRemoveClasses >> failureMessageForClassNames: classNames error: anError [ nextPutAll: anError messageText ] ] -{ #category : 'metadata' } -MCPToolRemoveClasses >> groupName [ - - ^ 'classes' -] - { #category : 'private - request' } MCPToolRemoveClasses >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolRemoveMethods.class.st b/src/MCP/MCPToolRemoveMethods.class.st index 66cc17c..69fb178 100644 --- a/src/MCP/MCPToolRemoveMethods.class.st +++ b/src/MCP/MCPToolRemoveMethods.class.st @@ -11,6 +11,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolRemoveMethods class >> groupName [ + + ^ self methodsGroupName +] + { #category : 'metadata' } MCPToolRemoveMethods class >> toolName [ @@ -162,12 +168,6 @@ MCPToolRemoveMethods >> forceRemoveSelectors: selectorSymbols from: aBehavior [ refactoring performChanges ] -{ #category : 'metadata' } -MCPToolRemoveMethods >> groupName [ - - ^ 'methods' -] - { #category : 'private - request' } MCPToolRemoveMethods >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolRepositoryOperation.class.st b/src/MCP/MCPToolRepositoryOperation.class.st index a74007e..eb1fbaf 100644 --- a/src/MCP/MCPToolRepositoryOperation.class.st +++ b/src/MCP/MCPToolRepositoryOperation.class.st @@ -11,6 +11,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolRepositoryOperation class >> groupName [ + + ^ self repositoriesGroupName +] + { #category : 'testing' } MCPToolRepositoryOperation class >> isAbstract [ @@ -77,12 +83,6 @@ MCPToolRepositoryOperation >> errorResultForCommandError: anError action: action ^ self errorResultText: anError messageText details: details ] -{ #category : 'metadata' } -MCPToolRepositoryOperation >> groupName [ - - ^ 'repositories' -] - { #category : 'private - schema' } MCPToolRepositoryOperation >> inputProperties [ diff --git a/src/MCP/MCPToolRewriteMethods.class.st b/src/MCP/MCPToolRewriteMethods.class.st index fb7dc2c..83bf796 100644 --- a/src/MCP/MCPToolRewriteMethods.class.st +++ b/src/MCP/MCPToolRewriteMethods.class.st @@ -12,6 +12,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolRewriteMethods class >> groupName [ + + ^ self methodsGroupName +] + { #category : 'metadata' } MCPToolRewriteMethods class >> toolName [ @@ -75,12 +81,6 @@ MCPToolRewriteMethods >> executeWithRequest: request [ rawRequest: request ] ] -{ #category : 'metadata' } -MCPToolRewriteMethods >> groupName [ - - ^ 'methods' -] - { #category : 'private - execution' } MCPToolRewriteMethods >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolRunTests.class.st b/src/MCP/MCPToolRunTests.class.st index c765268..8884d00 100644 --- a/src/MCP/MCPToolRunTests.class.st +++ b/src/MCP/MCPToolRunTests.class.st @@ -13,6 +13,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolRunTests class >> groupName [ + + ^ self testsGroupName +] + { #category : 'metadata' } MCPToolRunTests class >> toolName [ @@ -289,12 +295,6 @@ MCPToolRunTests >> failureMessageForTestNames: testNames error: anError [ nextPutAll: anError messageText ] ] -{ #category : 'metadata' } -MCPToolRunTests >> groupName [ - - ^ 'tests' -] - { #category : 'private - request' } MCPToolRunTests >> parsedRequestFromToolRequest: request [ diff --git a/src/MCP/MCPToolSearchClasses.class.st b/src/MCP/MCPToolSearchClasses.class.st index 51528e7..0a28adc 100644 --- a/src/MCP/MCPToolSearchClasses.class.st +++ b/src/MCP/MCPToolSearchClasses.class.st @@ -11,6 +11,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolSearchClasses class >> groupName [ + + ^ self classesGroupName +] + { #category : 'metadata' } MCPToolSearchClasses class >> toolName [ @@ -154,12 +160,6 @@ MCPToolSearchClasses >> fieldTextsForClassEntry: anEntry fieldName: fieldName [ ^ #( ) ] -{ #category : 'metadata' } -MCPToolSearchClasses >> groupName [ - - ^ 'classes' -] - { #category : 'private - filtering' } MCPToolSearchClasses >> matchesFieldFiltersOnClassEntry: anEntry queryRequest: queryRequest [ diff --git a/src/MCP/MCPToolSearchPackages.class.st b/src/MCP/MCPToolSearchPackages.class.st index c7681f5..2d0edf5 100644 --- a/src/MCP/MCPToolSearchPackages.class.st +++ b/src/MCP/MCPToolSearchPackages.class.st @@ -9,6 +9,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolSearchPackages class >> groupName [ + + ^ self packagesGroupName +] + { #category : 'metadata' } MCPToolSearchPackages class >> toolName [ @@ -73,12 +79,6 @@ MCPToolSearchPackages >> fieldTextsForPackageEntry: anEntry fieldName: fieldName ^ #( ) ] -{ #category : 'metadata' } -MCPToolSearchPackages >> groupName [ - - ^ 'packages' -] - { #category : 'private - filtering' } MCPToolSearchPackages >> matchesFieldFiltersOnPackageEntry: anEntry queryRequest: queryRequest [ diff --git a/src/MCP/MCPToolSearchRepositories.class.st b/src/MCP/MCPToolSearchRepositories.class.st index 52367a8..d268075 100644 --- a/src/MCP/MCPToolSearchRepositories.class.st +++ b/src/MCP/MCPToolSearchRepositories.class.st @@ -11,6 +11,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolSearchRepositories class >> groupName [ + + ^ self repositoriesGroupName +] + { #category : 'metadata' } MCPToolSearchRepositories class >> toolName [ @@ -108,12 +114,6 @@ MCPToolSearchRepositories >> fieldTextsForRepositoryEntry: anEntry fieldName: fi ^ #( ) ] -{ #category : 'metadata' } -MCPToolSearchRepositories >> groupName [ - - ^ 'repositories' -] - { #category : 'private - filtering' } MCPToolSearchRepositories >> matchesFieldFiltersOnRepositoryEntry: anEntry queryRequest: queryRequest [ diff --git a/src/MCP/MCPToolSearchTools.class.st b/src/MCP/MCPToolSearchTools.class.st index 7d7cb50..fcb67ce 100644 --- a/src/MCP/MCPToolSearchTools.class.st +++ b/src/MCP/MCPToolSearchTools.class.st @@ -14,6 +14,12 @@ Class { #tag : 'Tools' } +{ #category : 'metadata' } +MCPToolSearchTools class >> groupName [ + + ^ self toolCatalogGroupName +] + { #category : 'instance creation' } MCPToolSearchTools class >> newForRegistry: aToolRegistry [ @@ -100,12 +106,6 @@ MCPToolSearchTools >> executeWithRequest: request [ onError: [ :error :parsedRequest | self errorResultFor: error ] ] -{ #category : 'metadata' } -MCPToolSearchTools >> groupName [ - - ^ 'tool-catalog' -] - { #category : 'private - request' } MCPToolSearchTools >> parsedRequestFromToolRequest: request [