Skip to content

Commit 131d9f7

Browse files
committed
Conform list tools
1 parent 10a396b commit 131d9f7

File tree

14 files changed

+239
-153
lines changed

14 files changed

+239
-153
lines changed

README.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
149149
- `direction`: Sort direction ('asc', 'desc') (string, optional)
150150
- `since`: Filter by date (ISO 8601 timestamp) (string, optional)
151151
- `page`: Page number (number, optional)
152-
- `per_page`: Results per page (number, optional)
152+
- `perPage`: Results per page (number, optional)
153153

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

@@ -176,7 +176,7 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
176176

177177
- `owner`: Repository owner (string, required)
178178
- `repo`: Repository name (string, required)
179-
- `pull_number`: Pull request number (number, required)
179+
- `pullNumber`: Pull request number (number, required)
180180

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

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

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

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

202202
- `owner`: Repository owner (string, required)
203203
- `repo`: Repository name (string, required)
204-
- `pull_number`: Pull request number (number, required)
204+
- `pullNumber`: Pull request number (number, required)
205205

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

208208
- `owner`: Repository owner (string, required)
209209
- `repo`: Repository name (string, required)
210-
- `pull_number`: Pull request number (number, required)
210+
- `pullNumber`: Pull request number (number, required)
211211

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

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

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

221221
- `owner`: Repository owner (string, required)
222222
- `repo`: Repository name (string, required)
223-
- `pull_number`: Pull request number (number, required)
223+
- `pullNumber`: Pull request number (number, required)
224224

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

227227
- `owner`: Repository owner (string, required)
228228
- `repo`: Repository name (string, required)
229-
- `pull_number`: Pull request number (number, required)
229+
- `pullNumber`: Pull request number (number, required)
230230

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

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

241241
- **create_pull_request** - Create a new pull request
@@ -275,14 +275,14 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
275275
- `sort`: Sort field (string, optional)
276276
- `order`: Sort order (string, optional)
277277
- `page`: Page number (number, optional)
278-
- `per_page`: Results per page (number, optional)
278+
- `perPage`: Results per page (number, optional)
279279

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

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

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

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

315315
### Search
316316

@@ -320,22 +320,22 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
320320
- `sort`: Sort field (string, optional)
321321
- `order`: Sort order (string, optional)
322322
- `page`: Page number (number, optional)
323-
- `per_page`: Results per page (number, optional)
323+
- `perPage`: Results per page (number, optional)
324324

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

332332
### Code Scanning
333333

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

336336
- `owner`: Repository owner (string, required)
337337
- `repo`: Repository name (string, required)
338-
- `alert_number`: Alert number (number, required)
338+
- `alertNumber`: Alert number (number, required)
339339

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

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

400400
## License

cmd/mcpcurl/README.md

Lines changed: 1 addition & 1 deletion
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:

conformance/conformance_test.go

Lines changed: 85 additions & 0 deletions
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+
}

go.mod

Lines changed: 1 addition & 0 deletions
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.14.1
1011
github.com/migueleliasweb/go-github-mock v1.1.0

pkg/github/code_scanning.go

Lines changed: 2 additions & 2 deletions
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
}

pkg/github/code_scanning_test.go

Lines changed: 8 additions & 8 deletions
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)