Skip to content

Commit 6f7458a

Browse files
committed
Conform list tools
1 parent 989c43f commit 6f7458a

14 files changed

+239
-153
lines changed

Diff for: README.md

+20-20
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
150150
- `direction`: Sort direction ('asc', 'desc') (string, optional)
151151
- `since`: Filter by date (ISO 8601 timestamp) (string, optional)
152152
- `page`: Page number (number, optional)
153-
- `per_page`: Results per page (number, optional)
153+
- `perPage`: Results per page (number, optional)
154154

155155
- **update_issue** - Update an existing issue in a GitHub repository
156156

@@ -177,7 +177,7 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
177177

178178
- `owner`: Repository owner (string, required)
179179
- `repo`: Repository name (string, required)
180-
- `pull_number`: Pull request number (number, required)
180+
- `pullNumber`: Pull request number (number, required)
181181

182182
- **list_pull_requests** - List and filter repository pull requests
183183

@@ -186,14 +186,14 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
186186
- `state`: PR state (string, optional)
187187
- `sort`: Sort field (string, optional)
188188
- `direction`: Sort direction (string, optional)
189-
- `per_page`: Results per page (number, optional)
189+
- `perPage`: Results per page (number, optional)
190190
- `page`: Page number (number, optional)
191191

192192
- **merge_pull_request** - Merge a pull request
193193

194194
- `owner`: Repository owner (string, required)
195195
- `repo`: Repository name (string, required)
196-
- `pull_number`: Pull request number (number, required)
196+
- `pullNumber`: Pull request number (number, required)
197197
- `commit_title`: Title for the merge commit (string, optional)
198198
- `commit_message`: Message for the merge commit (string, optional)
199199
- `merge_method`: Merge method (string, optional)
@@ -202,41 +202,41 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
202202

203203
- `owner`: Repository owner (string, required)
204204
- `repo`: Repository name (string, required)
205-
- `pull_number`: Pull request number (number, required)
205+
- `pullNumber`: Pull request number (number, required)
206206

207207
- **get_pull_request_status** - Get the combined status of all status checks for a pull request
208208

209209
- `owner`: Repository owner (string, required)
210210
- `repo`: Repository name (string, required)
211-
- `pull_number`: Pull request number (number, required)
211+
- `pullNumber`: Pull request number (number, required)
212212

213213
- **update_pull_request_branch** - Update a pull request branch with the latest changes from the base branch
214214

215215
- `owner`: Repository owner (string, required)
216216
- `repo`: Repository name (string, required)
217-
- `pull_number`: Pull request number (number, required)
218-
- `expected_head_sha`: The expected SHA of the pull request's HEAD ref (string, optional)
217+
- `pullNumber`: Pull request number (number, required)
218+
- `expectedHeadSha`: The expected SHA of the pull request's HEAD ref (string, optional)
219219

220220
- **get_pull_request_comments** - Get the review comments on a pull request
221221

222222
- `owner`: Repository owner (string, required)
223223
- `repo`: Repository name (string, required)
224-
- `pull_number`: Pull request number (number, required)
224+
- `pullNumber`: Pull request number (number, required)
225225

226226
- **get_pull_request_reviews** - Get the reviews on a pull request
227227

228228
- `owner`: Repository owner (string, required)
229229
- `repo`: Repository name (string, required)
230-
- `pull_number`: Pull request number (number, required)
230+
- `pullNumber`: Pull request number (number, required)
231231

232232
- **create_pull_request_review** - Create a review on a pull request review
233233

234234
- `owner`: Repository owner (string, required)
235235
- `repo`: Repository name (string, required)
236-
- `pull_number`: Pull request number (number, required)
236+
- `pullNumber`: Pull request number (number, required)
237237
- `body`: Review comment text (string, optional)
238238
- `event`: Review action ('APPROVE', 'REQUEST_CHANGES', 'COMMENT') (string, required)
239-
- `commit_id`: SHA of commit to review (string, optional)
239+
- `commitId`: SHA of commit to review (string, optional)
240240
- `comments`: Line-specific comments array of objects, each object with path (string), position (number), and body (string) (array, optional)
241241

242242
- **create_pull_request** - Create a new pull request
@@ -276,14 +276,14 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
276276
- `sort`: Sort field (string, optional)
277277
- `order`: Sort order (string, optional)
278278
- `page`: Page number (number, optional)
279-
- `per_page`: Results per page (number, optional)
279+
- `perPage`: Results per page (number, optional)
280280

281281
- **create_repository** - Create a new GitHub repository
282282

283283
- `name`: Repository name (string, required)
284284
- `description`: Repository description (string, optional)
285285
- `private`: Whether the repository is private (boolean, optional)
286-
- `auto_init`: Auto-initialize with README (boolean, optional)
286+
- `autoInit`: Auto-initialize with README (boolean, optional)
287287

288288
- **get_file_contents** - Get contents of a file or directory
289289

@@ -311,7 +311,7 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
311311
- `sha`: Branch name, tag, or commit SHA (string, optional)
312312
- `path`: Only commits containing this file path (string, optional)
313313
- `page`: Page number (number, optional)
314-
- `per_page`: Results per page (number, optional)
314+
- `perPage`: Results per page (number, optional)
315315

316316
### Search
317317

@@ -321,22 +321,22 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
321321
- `sort`: Sort field (string, optional)
322322
- `order`: Sort order (string, optional)
323323
- `page`: Page number (number, optional)
324-
- `per_page`: Results per page (number, optional)
324+
- `perPage`: Results per page (number, optional)
325325

326326
- **search_users** - Search for GitHub users
327327
- `query`: Search query (string, required)
328328
- `sort`: Sort field (string, optional)
329329
- `order`: Sort order (string, optional)
330330
- `page`: Page number (number, optional)
331-
- `per_page`: Results per page (number, optional)
331+
- `perPage`: Results per page (number, optional)
332332

333333
### Code Scanning
334334

335335
- **get_code_scanning_alert** - Get a code scanning alert
336336

337337
- `owner`: Repository owner (string, required)
338338
- `repo`: Repository name (string, required)
339-
- `alert_number`: Alert number (number, required)
339+
- `alertNumber`: Alert number (number, required)
340340

341341
- **list_code_scanning_alerts** - List code scanning alerts for a repository
342342
- `owner`: Repository owner (string, required)
@@ -391,11 +391,11 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
391391
- **Get Repository Content for a Specific Pull Request**
392392
Retrieves the content of a repository at a specific path for a given pull request.
393393

394-
- **Template**: `repo://{owner}/{repo}/refs/pull/{pr_number}/head/contents{/path*}`
394+
- **Template**: `repo://{owner}/{repo}/refs/pull/{prNumber}/head/contents{/path*}`
395395
- **Parameters**:
396396
- `owner`: Repository owner (string, required)
397397
- `repo`: Repository name (string, required)
398-
- `pr_number`: Pull request number (string, required)
398+
- `prNumber`: Pull request number (string, required)
399399
- `path`: File or directory path (string, optional)
400400

401401
## License

Diff for: cmd/mcpcurl/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Use "mcpcurl tools [command] --help" for more information about a command.
7272
Get help for a specific tool:
7373

7474
```bash
75-
% ./mcpcurl --stdio-server-cmd "docker run -i --rm -e GITHUB_PERSONAL_ACCESS_TOKEN mcp/github" tools get_issue --help
75+
% ./mcpcurl --stdio-server-cmd "docker run -i --rm -e GITHUB_PERSONAL_ACCESS_TOKEN mcp/github" tools get_issue --help
7676
Get details of a specific issue in a GitHub repository.
7777

7878
Usage:

Diff for: conformance/conformance_test.go

+85
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"bufio"
77
"context"
88
"encoding/json"
9+
"errors"
910
"fmt"
1011
"io"
1112
"os"
@@ -17,6 +18,8 @@ import (
1718
"github.com/docker/docker/api/types/network"
1819
"github.com/docker/docker/client"
1920
"github.com/docker/docker/pkg/stdcopy"
21+
"github.com/google/go-cmp/cmp"
22+
"github.com/google/go-cmp/cmp/cmpopts"
2023
"github.com/stretchr/testify/require"
2124
)
2225

@@ -230,6 +233,69 @@ func diffNonNilFields(a, b interface{}, path string) string {
230233
return sb.String()
231234
}
232235

236+
func TestListTools(t *testing.T) {
237+
anthropicServer := start(t, anthropic)
238+
githubServer := start(t, github)
239+
240+
req := listToolsRequest{
241+
JSONRPC: "2.0",
242+
ID: 1,
243+
Method: "tools/list",
244+
}
245+
246+
require.NoError(t, anthropicServer.send(req))
247+
248+
var anthropicListToolsResponse listToolsResponse
249+
require.NoError(t, anthropicServer.receive(&anthropicListToolsResponse))
250+
251+
require.NoError(t, githubServer.send(req))
252+
253+
var ghListToolsResponse listToolsResponse
254+
require.NoError(t, githubServer.receive(&ghListToolsResponse))
255+
256+
require.NoError(t, isToolListSubset(anthropicListToolsResponse.Result, ghListToolsResponse.Result), "expected the github list tools response to be a subset of the anthropic list tools response")
257+
}
258+
259+
func isToolListSubset(subset, superset listToolsResult) error {
260+
// Build a map from tool name to Tool from the superset
261+
supersetMap := make(map[string]tool)
262+
for _, tool := range superset.Tools {
263+
supersetMap[tool.Name] = tool
264+
}
265+
266+
var err error
267+
for _, tool := range subset.Tools {
268+
sup, ok := supersetMap[tool.Name]
269+
if !ok {
270+
return fmt.Errorf("tool %q not found in superset", tool.Name)
271+
}
272+
273+
// Intentionally ignore the description fields because there are lots of slight differences.
274+
// if tool.Description != sup.Description {
275+
// return fmt.Errorf("description mismatch for tool %q, got %q expected %q", tool.Name, tool.Description, sup.Description)
276+
// }
277+
278+
// Ignore any description fields within the input schema properties for the same reason
279+
ignoreDescOpt := cmp.FilterPath(func(p cmp.Path) bool {
280+
// Look for a field named "Properties" somewhere in the path
281+
for _, ps := range p {
282+
if sf, ok := ps.(cmp.StructField); ok && sf.Name() == "Properties" {
283+
return true
284+
}
285+
}
286+
return false
287+
}, cmpopts.IgnoreMapEntries(func(k string, _ any) bool {
288+
return k == "description"
289+
}))
290+
291+
if diff := cmp.Diff(tool.InputSchema, sup.InputSchema, ignoreDescOpt); diff != "" {
292+
err = errors.Join(err, fmt.Errorf("inputSchema mismatch for tool %q:\n%s", tool.Name, diff))
293+
}
294+
}
295+
296+
return err
297+
}
298+
233299
type serverStartResult struct {
234300
server server
235301
err error
@@ -348,3 +414,22 @@ type serverInfo struct {
348414
Name string `json:"name"`
349415
Version string `json:"version"`
350416
}
417+
418+
type listToolsRequest = jsonRPRCRequest[struct{}]
419+
420+
type listToolsResponse = jsonRPRCResponse[listToolsResult]
421+
422+
type listToolsResult struct {
423+
Tools []tool `json:"tools"`
424+
}
425+
type tool struct {
426+
Name string `json:"name"`
427+
Description string `json:"description,omitempty"`
428+
InputSchema inputSchema `json:"inputSchema"`
429+
}
430+
431+
type inputSchema struct {
432+
Type string `json:"type"`
433+
Properties map[string]any `json:"properties,omitempty"`
434+
Required []string `json:"required,omitempty"`
435+
}

Diff for: go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.23.7
55
require (
66
github.com/aws/smithy-go v1.22.3
77
github.com/docker/docker v28.0.4+incompatible
8+
github.com/google/go-cmp v0.7.0
89
github.com/google/go-github/v69 v69.2.0
910
github.com/mark3labs/mcp-go v0.18.0
1011
github.com/migueleliasweb/go-github-mock v1.1.0

Diff for: pkg/github/code_scanning.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func getCodeScanningAlert(client *github.Client, t translations.TranslationHelpe
2424
mcp.Required(),
2525
mcp.Description("The name of the repository."),
2626
),
27-
mcp.WithNumber("alert_number",
27+
mcp.WithNumber("alertNumber",
2828
mcp.Required(),
2929
mcp.Description("The number of the alert."),
3030
),
@@ -38,7 +38,7 @@ func getCodeScanningAlert(client *github.Client, t translations.TranslationHelpe
3838
if err != nil {
3939
return mcp.NewToolResultError(err.Error()), nil
4040
}
41-
alertNumber, err := requiredInt(request, "alert_number")
41+
alertNumber, err := requiredInt(request, "alertNumber")
4242
if err != nil {
4343
return mcp.NewToolResultError(err.Error()), nil
4444
}

Diff for: pkg/github/code_scanning_test.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ func Test_GetCodeScanningAlert(t *testing.T) {
2222
assert.NotEmpty(t, tool.Description)
2323
assert.Contains(t, tool.InputSchema.Properties, "owner")
2424
assert.Contains(t, tool.InputSchema.Properties, "repo")
25-
assert.Contains(t, tool.InputSchema.Properties, "alert_number")
26-
assert.ElementsMatch(t, tool.InputSchema.Required, []string{"owner", "repo", "alert_number"})
25+
assert.Contains(t, tool.InputSchema.Properties, "alertNumber")
26+
assert.ElementsMatch(t, tool.InputSchema.Required, []string{"owner", "repo", "alertNumber"})
2727

2828
// Setup mock alert for success case
2929
mockAlert := &github.Alert{
@@ -50,9 +50,9 @@ func Test_GetCodeScanningAlert(t *testing.T) {
5050
),
5151
),
5252
requestArgs: map[string]interface{}{
53-
"owner": "owner",
54-
"repo": "repo",
55-
"alert_number": float64(42),
53+
"owner": "owner",
54+
"repo": "repo",
55+
"alertNumber": float64(42),
5656
},
5757
expectError: false,
5858
expectedAlert: mockAlert,
@@ -69,9 +69,9 @@ func Test_GetCodeScanningAlert(t *testing.T) {
6969
),
7070
),
7171
requestArgs: map[string]interface{}{
72-
"owner": "owner",
73-
"repo": "repo",
74-
"alert_number": float64(9999),
72+
"owner": "owner",
73+
"repo": "repo",
74+
"alertNumber": float64(9999),
7575
},
7676
expectError: true,
7777
expectedErrMsg: "failed to get alert",

0 commit comments

Comments
 (0)