Merge pull request #142 from maartenvandiemen/feature/pass-toolsets
Pass GitHub MCP Tools
This commit is contained in:
6
.github/workflows/check-dist.yml
vendored
6
.github/workflows/check-dist.yml
vendored
@@ -28,11 +28,11 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
id: checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
id: setup-node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: npm
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
- if: ${{ failure() && steps.diff.outcome == 'failure' }}
|
||||
name: Upload Artifact
|
||||
id: upload
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
|
||||
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -20,11 +20,11 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
id: checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
id: setup-node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: npm
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
id: checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
@@ -108,7 +108,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
id: checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -30,19 +30,19 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
id: checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Initialize CodeQL
|
||||
id: initialize
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
source-root: src
|
||||
|
||||
- name: Autobuild
|
||||
id: autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
id: analyze
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
||||
6
.github/workflows/licensed.yml
vendored
6
.github/workflows/licensed.yml
vendored
@@ -27,11 +27,11 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
id: checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
id: setup-node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: npm
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
- name: Setup Ruby
|
||||
id: setup-ruby
|
||||
uses: ruby/setup-ruby@829114fc20da43a41d27359103ec7a63020954d4
|
||||
uses: ruby/setup-ruby@8aeb6ff8030dd539317f8e1769a044873b56ea71
|
||||
with:
|
||||
ruby-version: ruby
|
||||
|
||||
|
||||
2
.github/workflows/linter.yml
vendored
2
.github/workflows/linter.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
id: checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
19
README.md
19
README.md
@@ -201,6 +201,25 @@ steps:
|
||||
github-mcp-token: ${{ secrets.USER_PAT }} # or a ghs_ installation token
|
||||
```
|
||||
|
||||
#### Configuring GitHub MCP Toolsets
|
||||
|
||||
By default, the GitHub MCP server provides a standard set of tools (`context`, `repos`, `issues`, `pull_requests`, `users`). You can customize which toolsets are available by specifying the `github-mcp-toolsets` parameter:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- name: AI Inference with Custom Toolsets
|
||||
id: inference
|
||||
uses: actions/ai-inference@v2
|
||||
with:
|
||||
prompt: 'Analyze recent workflow runs and check security alerts'
|
||||
enable-github-mcp: true
|
||||
token: ${{ secrets.USER_PAT }}
|
||||
github-mcp-toolsets: 'repos,issues,pull_requests,actions,code_security'
|
||||
```
|
||||
|
||||
**Available toolsets:**
|
||||
See: [Tool configuration](https://github.com/github/github-mcp-server/blob/main/README.md#tool-configuration)
|
||||
|
||||
When MCP is enabled, the AI model will have access to GitHub tools and can
|
||||
perform actions like searching issues and PRs.
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ describe('main.ts', () => {
|
||||
|
||||
await run()
|
||||
|
||||
expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token')
|
||||
expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token', '')
|
||||
expect(mockMcpInference).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
messages: [
|
||||
@@ -222,7 +222,7 @@ describe('main.ts', () => {
|
||||
|
||||
await run()
|
||||
|
||||
expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token')
|
||||
expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token', '')
|
||||
expect(mockSimpleInference).toHaveBeenCalled()
|
||||
expect(mockMcpInference).not.toHaveBeenCalled()
|
||||
expect(core.warning).toHaveBeenCalledWith('MCP connection failed, falling back to simple inference')
|
||||
|
||||
@@ -113,6 +113,40 @@ describe('mcp.ts', () => {
|
||||
expect(result?.tools).toHaveLength(0)
|
||||
expect(core.info).toHaveBeenCalledWith('Retrieved 0 tools from GitHub MCP server')
|
||||
})
|
||||
|
||||
it('uses default toolsets when toolsets parameter is not provided', async () => {
|
||||
const token = 'test-token'
|
||||
|
||||
mockConnect.mockResolvedValue(undefined)
|
||||
mockListTools.mockResolvedValue({tools: []})
|
||||
|
||||
await connectToGitHubMCP(token)
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith('Using default GitHub MCP toolsets')
|
||||
})
|
||||
|
||||
it('uses custom toolsets when toolsets parameter is provided', async () => {
|
||||
const token = 'test-token'
|
||||
const toolsets = 'repos,issues,pull_requests,actions'
|
||||
|
||||
mockConnect.mockResolvedValue(undefined)
|
||||
mockListTools.mockResolvedValue({tools: []})
|
||||
|
||||
await connectToGitHubMCP(token, toolsets)
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith('Using GitHub MCP toolsets: repos,issues,pull_requests,actions')
|
||||
})
|
||||
|
||||
it('ignores empty toolsets parameter', async () => {
|
||||
const token = 'test-token'
|
||||
|
||||
mockConnect.mockResolvedValue(undefined)
|
||||
mockListTools.mockResolvedValue({tools: []})
|
||||
|
||||
await connectToGitHubMCP(token, ' ')
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith('Using default GitHub MCP toolsets')
|
||||
})
|
||||
})
|
||||
|
||||
describe('executeToolCall', () => {
|
||||
|
||||
@@ -58,6 +58,10 @@ inputs:
|
||||
description: The token to use for GitHub MCP server (defaults to the main token if not specified). This must be a PAT for MCP to work.
|
||||
required: false
|
||||
default: ''
|
||||
github-mcp-toolsets:
|
||||
description: 'Comma-separated list of toolsets to enable for GitHub MCP (e.g., "repos,issues,pull_requests,actions"). Use "all" for all toolsets, "default" for default set. If not specified, uses default toolsets (context,repos,issues,pull_requests,users).'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
# Define your outputs here.
|
||||
outputs:
|
||||
|
||||
22
dist/index.js
generated
vendored
22
dist/index.js
generated
vendored
@@ -42717,15 +42717,24 @@ class StreamableHTTPClientTransport {
|
||||
/**
|
||||
* Connect to the GitHub MCP server and retrieve available tools
|
||||
*/
|
||||
async function connectToGitHubMCP(token) {
|
||||
async function connectToGitHubMCP(token, toolsets) {
|
||||
const githubMcpUrl = 'https://api.githubcopilot.com/mcp/';
|
||||
coreExports.info('Connecting to GitHub MCP server...');
|
||||
const headers = {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'X-MCP-Readonly': 'true',
|
||||
};
|
||||
// Add toolsets header if specified
|
||||
if (toolsets && toolsets.trim() !== '') {
|
||||
headers['X-MCP-Toolsets'] = toolsets;
|
||||
coreExports.info(`Using GitHub MCP toolsets: ${toolsets}`);
|
||||
}
|
||||
else {
|
||||
coreExports.info('Using default GitHub MCP toolsets');
|
||||
}
|
||||
const transport = new StreamableHTTPClientTransport(new URL(githubMcpUrl), {
|
||||
requestInit: {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'X-MCP-Readonly': 'true',
|
||||
},
|
||||
headers,
|
||||
},
|
||||
});
|
||||
const client = new Client({
|
||||
@@ -52653,13 +52662,14 @@ async function run() {
|
||||
}
|
||||
// Get GitHub MCP token (use dedicated token if provided, otherwise fall back to main token)
|
||||
const githubMcpToken = coreExports.getInput('github-mcp-token') || token;
|
||||
const githubMcpToolsets = coreExports.getInput('github-mcp-toolsets');
|
||||
const endpoint = coreExports.getInput('endpoint');
|
||||
// Build the inference request with pre-processed messages and response format
|
||||
const inferenceRequest = buildInferenceRequest(promptConfig, systemPrompt, prompt, modelName, promptConfig?.modelParameters?.temperature, promptConfig?.modelParameters?.topP, maxTokens, endpoint, token);
|
||||
const enableMcp = coreExports.getBooleanInput('enable-github-mcp') || false;
|
||||
let modelResponse = null;
|
||||
if (enableMcp) {
|
||||
const mcpClient = await connectToGitHubMCP(githubMcpToken);
|
||||
const mcpClient = await connectToGitHubMCP(githubMcpToken, githubMcpToolsets);
|
||||
if (mcpClient) {
|
||||
modelResponse = await mcpInference(inferenceRequest, mcpClient);
|
||||
}
|
||||
|
||||
2
dist/index.js.map
generated
vendored
2
dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@@ -61,6 +61,7 @@ export async function run(): Promise<void> {
|
||||
|
||||
// Get GitHub MCP token (use dedicated token if provided, otherwise fall back to main token)
|
||||
const githubMcpToken = core.getInput('github-mcp-token') || token
|
||||
const githubMcpToolsets = core.getInput('github-mcp-toolsets')
|
||||
|
||||
const endpoint = core.getInput('endpoint')
|
||||
|
||||
@@ -82,7 +83,7 @@ export async function run(): Promise<void> {
|
||||
let modelResponse: string | null = null
|
||||
|
||||
if (enableMcp) {
|
||||
const mcpClient = await connectToGitHubMCP(githubMcpToken)
|
||||
const mcpClient = await connectToGitHubMCP(githubMcpToken, githubMcpToolsets)
|
||||
|
||||
if (mcpClient) {
|
||||
modelResponse = await mcpInference(inferenceRequest, mcpClient)
|
||||
|
||||
20
src/mcp.ts
20
src/mcp.ts
@@ -35,17 +35,27 @@ export interface GitHubMCPClient {
|
||||
/**
|
||||
* Connect to the GitHub MCP server and retrieve available tools
|
||||
*/
|
||||
export async function connectToGitHubMCP(token: string): Promise<GitHubMCPClient | null> {
|
||||
export async function connectToGitHubMCP(token: string, toolsets?: string): Promise<GitHubMCPClient | null> {
|
||||
const githubMcpUrl = 'https://api.githubcopilot.com/mcp/'
|
||||
|
||||
core.info('Connecting to GitHub MCP server...')
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'X-MCP-Readonly': 'true',
|
||||
}
|
||||
|
||||
// Add toolsets header if specified
|
||||
if (toolsets && toolsets.trim() !== '') {
|
||||
headers['X-MCP-Toolsets'] = toolsets
|
||||
core.info(`Using GitHub MCP toolsets: ${toolsets}`)
|
||||
} else {
|
||||
core.info('Using default GitHub MCP toolsets')
|
||||
}
|
||||
|
||||
const transport = new StreamableHTTPClientTransport(new URL(githubMcpUrl), {
|
||||
requestInit: {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'X-MCP-Readonly': 'true',
|
||||
},
|
||||
headers,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user