Skip to content

Commit 60e87cb

Browse files
committed
mcb
1 parent f875c2e commit 60e87cb

7 files changed

Lines changed: 89 additions & 10 deletions

File tree

‎mcpb/manifest.json‎

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"display_name": "CodeAnt AI",
55
"version": "0.5.0",
66
"description": "Drive CodeAnt AI security scans and code review from Claude — org-wide secret triage, cross-repo SAST/SCA findings, on-demand scans, and local PR review.",
7-
"long_description": "CodeAnt AI inside Claude. Ask things like \"how many critical SAST findings do I have across my org?\", \"show every exposed secret in payments-service\", or \"review my staged changes\" — Claude calls the CodeAnt API directly via this MCP server.\n\nIncludes 11 read-only tools (orgs, repos, scan history, scan metadata, findings, dismissed alerts, PRs, comments, comment search, local review) and 2 opt-in write tools (trigger a scan, resolve a PR conversation) gated behind a setting.\n\nRequires a CodeAnt account. Sign up at https://codeant.ai and grab an API key from your settings page.",
7+
"long_description": "CodeAnt AI inside Claude. Ask things like \"how many critical SAST findings do I have across my org?\", \"show every exposed secret in payments-service\", or \"review my staged changes\" — Claude calls the CodeAnt API directly via this MCP server.\n\nIncludes 11 read-only tools (orgs, repos, scan history, scan metadata, findings, dismissed alerts, PRs, comments, comment search, local review) and 2 opt-in write tools (trigger a scan, resolve a PR conversation) gated behind a setting.\n\nRequires a CodeAnt account. Sign up at https://codeant.ai and grab an API key from your settings page.\n\nCollects anonymous usage telemetry via PostHog by default; set CODEANT_TELEMETRY_DISABLED=1 to opt out.",
88
"author": {
99
"name": "CodeAnt AI",
1010
"email": "support@codeant.ai",
@@ -28,7 +28,7 @@
2828
"pull-requests"
2929
],
3030
"privacy_policies": [
31-
"https://codeant.ai/privacy"
31+
"https://www.codeant.ai/privacy-policy"
3232
],
3333
"icon": "icon.png",
3434
"server": {
@@ -40,7 +40,15 @@
4040
"env": {
4141
"CODEANT_API_TOKEN": "${user_config.api_token}",
4242
"CODEANT_API_URL": "${user_config.base_url}",
43-
"CODEANT_READ_ONLY": "${user_config.read_only}"
43+
"CODEANT_READ_ONLY": "${user_config.read_only}",
44+
"GITHUB_TOKEN": "${user_config.github_token}",
45+
"GITLAB_TOKEN": "${user_config.gitlab_token}",
46+
"BITBUCKET_TOKEN": "${user_config.bitbucket_token}",
47+
"AZURE_DEVOPS_TOKEN": "${user_config.azure_devops_token}",
48+
"GITHUB_API_URL": "${user_config.github_api_url}",
49+
"GITLAB_URL": "${user_config.gitlab_url}",
50+
"BITBUCKET_URL": "${user_config.bitbucket_url}",
51+
"AZURE_DEVOPS_ORG_URL": "${user_config.azure_devops_org_url}"
4452
}
4553
}
4654
},
@@ -88,6 +96,66 @@
8896
"description": "When enabled, write tools (trigger scan, resolve PR thread) are hidden. Recommended.",
8997
"required": false,
9098
"default": true
99+
},
100+
"github_token": {
101+
"type": "string",
102+
"title": "GitHub token",
103+
"description": "Personal access token for GitHub. Also accepts the GH_TOKEN environment variable. Leave blank to fall back to the gh CLI.",
104+
"required": false,
105+
"sensitive": true,
106+
"default": ""
107+
},
108+
"gitlab_token": {
109+
"type": "string",
110+
"title": "GitLab token",
111+
"description": "Personal access token for GitLab. Leave blank to fall back to the glab CLI.",
112+
"required": false,
113+
"sensitive": true,
114+
"default": ""
115+
},
116+
"bitbucket_token": {
117+
"type": "string",
118+
"title": "Bitbucket token",
119+
"description": "App password or access token for Bitbucket.",
120+
"required": false,
121+
"sensitive": true,
122+
"default": ""
123+
},
124+
"azure_devops_token": {
125+
"type": "string",
126+
"title": "Azure DevOps token",
127+
"description": "Personal access token for Azure DevOps. Also accepts AZURE_DEVOPS_PAT.",
128+
"required": false,
129+
"sensitive": true,
130+
"default": ""
131+
},
132+
"github_api_url": {
133+
"type": "string",
134+
"title": "GitHub API URL",
135+
"description": "Override for GitHub Enterprise Server (e.g. https://github.example.com/api/v3). Leave blank for GitHub.com.",
136+
"required": false,
137+
"default": ""
138+
},
139+
"gitlab_url": {
140+
"type": "string",
141+
"title": "GitLab URL",
142+
"description": "Override for a self-hosted GitLab instance (e.g. https://gitlab.example.com). Leave blank for GitLab.com.",
143+
"required": false,
144+
"default": ""
145+
},
146+
"bitbucket_url": {
147+
"type": "string",
148+
"title": "Bitbucket URL",
149+
"description": "Override for a self-hosted Bitbucket Server instance. Leave blank for Bitbucket Cloud.",
150+
"required": false,
151+
"default": ""
152+
},
153+
"azure_devops_org_url": {
154+
"type": "string",
155+
"title": "Azure DevOps organization URL",
156+
"description": "Full URL to your Azure DevOps organization (e.g. https://dev.azure.com/myorg).",
157+
"required": false,
158+
"default": ""
91159
}
92160
}
93161
}

‎src/mcp/server.js‎

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,7 @@ async function ensureAuthenticated() {
7373
return;
7474
}
7575

76-
console.error('[codeant-mcp] No API token configured — opening browser for sign-in.');
77-
try {
78-
await runLoginFlow();
79-
console.error('[codeant-mcp] Login complete.');
80-
} catch (err) {
81-
console.error(`[codeant-mcp] Login failed: ${err.message}. The server will start anyway; call the codeant_login tool to retry.`);
82-
}
76+
console.error('[codeant-mcp] No API token configured. Call the codeant_login tool to sign in, or set CODEANT_API_TOKEN.');
8377
}
8478

8579
export async function startMcpServer() {

‎src/tools/globTool.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import path from 'path';
2+
import { assertInsideCwd } from './pathUtils.js';
23

34
export async function globTool(args, cwd) {
45
const { globSync } = await import('glob');
56
const pattern = path.resolve(cwd, args.pattern);
7+
assertInsideCwd(pattern.replace(/[*?{[\\].*$/, '') || cwd, cwd);
68
const matches = globSync(pattern);
79
if (!matches.length) return 'No files found';
810
return matches.map(m => path.relative(cwd, m)).join('\n');

‎src/tools/grepTool.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { spawn } from 'child_process';
22
import path from 'path';
3+
import { assertInsideCwd } from './pathUtils.js';
34

45
export async function grepTool(args, cwd) {
56
const target = args.path ? path.resolve(cwd, args.path) : cwd;
7+
assertInsideCwd(target, cwd);
68
const result = await new Promise((resolve) => {
79
const proc = spawn('grep', ['-rn', args.pattern, target], {
810
cwd,

‎src/tools/lsTool.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import fs from 'fs';
22
import path from 'path';
3+
import { assertInsideCwd } from './pathUtils.js';
34

45
export async function lsTool(args, cwd) {
56
const dirPath = args.path ? path.resolve(cwd, args.path) : cwd;
7+
assertInsideCwd(dirPath, cwd);
68
return fs.readdirSync(dirPath).sort().join('\n');
79
}

‎src/tools/pathUtils.js‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import path from 'path';
2+
3+
export function assertInsideCwd(resolved, cwd) {
4+
const base = path.resolve(cwd);
5+
const target = path.resolve(resolved);
6+
if (target !== base && !target.startsWith(base + path.sep)) {
7+
throw new Error(`Access denied: path is outside the working directory`);
8+
}
9+
}

‎src/tools/readTool.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import fs from 'fs';
22
import path from 'path';
3+
import { assertInsideCwd } from './pathUtils.js';
34

45
export async function readTool(args, cwd) {
56
const filePath = path.resolve(cwd, args.file_path);
7+
assertInsideCwd(filePath, cwd);
68
const content = await fs.promises.readFile(filePath, 'utf8');
79
const lines = content.split('\n');
810
const offset = args.offset || 1;

0 commit comments

Comments
 (0)