Skip to content

feat: ql-mcp-client Phase 3(A) — use tool, use resource, use prompt subcommands with shared primitives library#233

Merged
data-douser merged 5 commits intonextfrom
copilot/add-prompt-resource-tool-subcommands
Apr 8, 2026
Merged

feat: ql-mcp-client Phase 3(A) — use tool, use resource, use prompt subcommands with shared primitives library#233
data-douser merged 5 commits intonextfrom
copilot/add-prompt-resource-tool-subcommands

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 7, 2026

After Phase 2, the Go binary can run integration tests and list primitives, but cannot call an individual prompt, resource, or tool from the CLI. Additionally, the integration test runner had its own inline MCP invocation logic that should be shared.

Shared primitives library (client/internal/mcp/primitives.go)

Single source of truth for calling MCP primitives, used by both CLI subcommands and the integration test runner:

  • CallTool(ctx, client, name, args) → *ToolResult
  • ReadResource(ctx, client, uri) → *ResourceContent
  • GetPrompt(ctx, client, name, args) → *PromptMessages
  • ListTools, ListResources, ListPrompts — typed list wrappers
  • Format{JSON,*Text,*Markdown} — output formatters for all result types

New use subcommands

ql-mcp-client use tool codeql_resolve_languages --format json
ql-mcp-client use tool sarif_list_rules --arg sarifPath=/path/to/results.sarif
ql-mcp-client use resource codeql://server/tools --format markdown
ql-mcp-client use prompt explain_codeql_query --arg queryPath=/path/to/query.ql --arg language=javascript

All three support --format json|text|markdown.

Client wrapper additions (client/internal/mcp/client.go)

  • Added GetPrompt(ctx, name, args) and ReadResource(ctx, uri) to both innerClient interface and Client wrapper
  • Extended mock in client_test.go accordingly

Integration test runner refactoring

  • client/cmd/integration_tests.gomcpToolCaller.CallToolRaw and ListToolNames now delegate to the shared primitives library instead of inline MCP SDK calls
  • client/internal/testing/runner.go — uses mcpprim.ContentBlock from primitives (eliminated duplicated type definition)

Infrastructure

  • Updated .github/actions/setup-codeql-environment/action.yml default go-version from 1.211.25 to match client/go.mod (go 1.25.6)

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • update.code.visualstudio.com
    • Triggering command: /opt/hostedtoolcache/node/24.13.0/x64/bin/node node scripts/download-vscode.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI and others added 3 commits April 7, 2026 23:53
- Add client/internal/mcp/primitives.go with shared CallTool, ReadResource,
  GetPrompt, ListTools, ListResources, ListPrompts functions and formatters
- Add client/internal/mcp/primitives_test.go with comprehensive unit tests
- Add GetPrompt and ReadResource to client/internal/mcp/client.go
- Add client/cmd/use.go parent subcommand
- Add client/cmd/use_tool.go, use_resource.go, use_prompt.go subcommands
- Add client/cmd/use_test.go with CLI tests
- Refactor client/cmd/integration_tests.go to use shared primitives
- Refactor client/internal/testing/runner.go to use shared ContentBlock type
- Support --format json|text|markdown across all subcommands

Agent-Logs-Url: https://github.com/advanced-security/codeql-development-mcp-server/sessions/78e52dbe-a4f9-4a7a-b042-c14ad89421b1

Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com>
…1.25

The client/go.mod requires go 1.25.6 but the setup action defaulted to
Go 1.21, causing build failures when npm run build-and-test uses the
action's default Go version.

Agent-Logs-Url: https://github.com/advanced-security/codeql-development-mcp-server/sessions/78e52dbe-a4f9-4a7a-b042-c14ad89421b1

Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com>
Copilot AI changed the title [WIP] Add subcommands for prompt, resource, and tool in ql-mcp-client feat: ql-mcp-client Phase 3(A) — use tool, use resource, use prompt subcommands with shared primitives library Apr 8, 2026
Copilot AI requested a review from data-douser April 8, 2026 00:18
Copy link
Copy Markdown
Collaborator

@data-douser data-douser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End-to-End Validation Review — ql-mcp-client Phase 3(A) (#222)

Built the Go binary, built the MCP server, and ran all subcommands against a live MCP server via stdio transport.

Unit Tests

$ go test ./...
ok  client/cmd                 0.477s
ok  client/internal/mcp        3.837s
ok  client/internal/testing    0.719s

E2E Results (live MCP server via stdio)

Test Command Result
use tool (no args) use tool codeql_resolve_languages --format text ✅ 15 languages returned
use tool (JSON) use tool codeql_resolve_languages --format json ✅ Valid JSON with content array
use tool (markdown) use tool codeql_resolve_languages --format markdown ✅ Output rendered
use tool (with --arg) use tool sarif_list_rules --arg sarifPath=query-results.sarif ✅ 1 rule, 2 results returned
use tool (wrong arg name) use tool codeql_pack_ls --arg workspacePath=. [ERROR] + validation message
use resource (text) use resource codeql://server/tools --format text ✅ Full tool reference with URI/MIME metadata
use resource (JSON) use resource codeql://server/overview --format json ✅ Valid JSON with contents array
use resource (markdown) use resource codeql://server/prompts --format markdown ✅ 58 lines of markdown
use prompt (with args, text) use prompt explain_codeql_query --arg queryPath=... --arg language=javascript --format text ✅ 169 lines, role/content rendered
use prompt (JSON) Same prompt --format json ✅ Valid JSON, messages array with 1 entry
use prompt (markdown) Same prompt --format markdown ✅ 169 lines with ### user headers
Missing required args use tool, use resource, use prompt (no positional arg) ✅ All exit 1 with "accepts 1 arg(s)"
Nonexistent resource use resource codeql://nonexistent ✅ Exit 1, clear error message
Nonexistent prompt use prompt nonexistent_prompt ✅ Exit 1, clear error message

Bug Found: use tool exit code on MCP-level errors

$ ./gh-ql-mcp-client use tool nonexistent_tool --format text; echo "exit: $?"
[ERROR]
MCP error -32602: Tool nonexistent_tool not found
exit: 0    # ← should be non-zero

When a tool call returns IsError: true in the ToolResult, outputToolResult() in use_tool.go still returns nil, so the CLI exits 0. Compare with use resource and use prompt which both correctly exit 1 on server errors.

Suggested fix in outputToolResult():

func outputToolResult(result *mcpclient.ToolResult) error {
    // ... format and print ...
    if result.IsError {
        return fmt.Errorf("tool returned error")
    }
    return nil
}

Minor Nit: cutString duplicates strings.Cut

cutString() in client/cmd/use.go reimplements strings.Cut (available since Go 1.18; module targets Go 1.25). Could simplify by replacing with strings.Cut(a, "=") and deleting the 7-line function.

Acceptance Criteria Cross-Reference (#222)

# Criterion Status
1 use tool <name> --arg k=v calls tool, prints response ✅ Verified E2E
2 use resource <uri> reads resource, prints content ✅ Verified E2E
3 use prompt <name> --arg k=v gets prompt, prints messages ✅ Verified E2E
4 All three support --format json|text|markdown ✅ All 9 combinations verified
5 Test runner uses primitives.go for all MCP calls ✅ Code review confirmed
6 No duplicated MCP invocation logic ✅ Single source of truth in primitives.go
7 go test ./... passes ✅ All 3 packages pass
8 Existing integration test fixtures pass ⚠️ Not run (full suite requires CI infra)
9 npm run build-and-test ⚠️ Not run (CI scope)

Summary

Implementation is solid. All CLI subcommands work correctly against a live MCP server. The shared primitives library architecture is clean. One bug found (exit code 0 on tool errors) and one minor nit (cutStringstrings.Cut). Requesting changes for the exit code issue since it affects scripting/automation use cases.

…tring with strings.Cut

- outputToolResult() now returns an error when result.IsError is true,
  so the CLI exits 1 on MCP-level tool errors (e.g. nonexistent tool)
- Replace custom cutString() with stdlib strings.Cut (available since Go 1.18)

Agent-Logs-Url: https://github.com/advanced-security/codeql-development-mcp-server/sessions/cc25a44f-aecb-453c-be59-8064533607d3

Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 8, 2026

End-to-End Validation Review — ql-mcp-client Phase 3(A) (#222)

Built the Go binary, built the MCP server, and ran all subcommands against a live MCP server via stdio transport....

Fixed both issues in fc24b2c:

  1. Exit code bug: outputToolResult() now returns fmt.Errorf("tool returned error") when result.IsError is true — the output is still printed first so users see the error message, then the CLI exits 1.

  2. cutStringstrings.Cut: Replaced the custom implementation with strings.Cut from stdlib and removed the 7-line function.

Copilot AI requested a review from data-douser April 8, 2026 01:07
Copy link
Copy Markdown
Collaborator

@data-douser data-douser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-validation after fixes (fc24b2c)

Both issues from the previous review have been resolved. Rebuilt the binary, re-ran unit tests and E2E tests against a live MCP server.

Fix 1: Exit code on IsError tool results ✅

# Before fix: exit 0
# After fix:
$ ./gh-ql-mcp-client use tool nonexistent_tool --format text; echo "exit: $?"
[ERROR]
MCP error -32602: Tool nonexistent_tool not found
tool returned error
exit: 1  ✅

$ ./gh-ql-mcp-client use tool nonexistent_tool --format json 2>/dev/null
{"content":[...],"isError":true}   # JSON on stdout
# "tool returned error" on stderr — clean separation ✅

Fix 2: cutStringstrings.Cut

Custom cutString function removed, both parseArgs and parseArgsAny now use strings.Cut.

Re-validation results

Test Result
go test ./... ✅ All 3 packages pass
use tool codeql_resolve_languages (text) ✅ 15 languages, exit 0
use tool sarif_list_rules --arg sarifPath=... ✅ Results returned, exit 0
use tool nonexistent_tool [ERROR], exit 1
use tool nonexistent --format json ✅ JSON on stdout, error on stderr, exit 1
use resource codeql://server/overview ✅ 100 lines, exit 0
use prompt explain_codeql_query --arg ... ✅ 168 lines, exit 0

All previously identified issues are fixed. Happy paths unaffected.

@data-douser data-douser marked this pull request as ready for review April 8, 2026 01:20
@data-douser data-douser requested review from a team and enyil as code owners April 8, 2026 01:20
Copilot AI review requested due to automatic review settings April 8, 2026 01:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@data-douser data-douser merged commit b777fc3 into next Apr 8, 2026
20 of 21 checks passed
@data-douser data-douser deleted the copilot/add-prompt-resource-tool-subcommands branch April 8, 2026 01:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ql-mcp-client Phase 3(A): use prompt, use resource, and use tool subcommands with shared primitive-calling library

3 participants