diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 68688b83..e3fc674c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2,7 +2,8 @@ on: pull_request: jobs: - validate-servers: + validate: + name: "Code and Server Validation" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -21,6 +22,9 @@ jobs: version: 3.x repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Run unit tests + run: task unittest + - name: Get changed servers shell: bash run: | diff --git a/.github/workflows/security-review-changes.yaml b/.github/workflows/security-review-changes.yaml new file mode 100644 index 00000000..1703902e --- /dev/null +++ b/.github/workflows/security-review-changes.yaml @@ -0,0 +1,477 @@ +name: Security Review (Changes) + +on: + workflow_dispatch: + inputs: + pull_request_number: + description: "Pull request number to review" + required: true + default: "" + agent: + description: "Optional reviewer agent (claude or codex)." + required: false + default: "" + model: + description: "Optional reviewer model override." + required: false + default: "" + timeout_secs: + description: "Optional reviewer timeout in seconds (defaults to 1800)." + required: false + default: "" + force_review: + description: "Force re-review even if already completed for this commit" + required: false + type: boolean + default: false + # pull_request: + # types: + # - opened + # - synchronize + # - reopened + # - ready_for_review + # - labeled + +concurrency: + group: security-review-changes-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: false + +jobs: + pr-security-review: + name: Pull Request Security Review + runs-on: ubuntu-24.04 + if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' + permissions: + contents: read + checks: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Checkout pull request + if: github.event_name == 'workflow_dispatch' && github.event.inputs.pull_request_number != '' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh pr checkout "${{ github.event.inputs.pull_request_number }}" + + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker + uses: docker/setup-docker-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Set up Docker Compose + uses: docker/setup-compose-action@v1 + + - name: Resolve comparison commits + id: revision + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + + if [ "${{ github.event_name }}" = "pull_request" ]; then + # For pull_request events, GitHub checks out a merge commit. + # Use the merge commit for the check, but review the actual PR changes. + check_sha="${{ github.sha }}" + head_sha=$(git rev-parse HEAD^2) + base_sha=$(git merge-base HEAD^1 HEAD^2) + pr_number="${{ github.event.pull_request.number }}" + else + # For workflow_dispatch, we've checked out the PR branch. + git fetch origin "${{ github.event.repository.default_branch }}" + check_sha=$(git rev-parse HEAD) + head_sha=$(git rev-parse HEAD) + base_sha=$(git merge-base HEAD "origin/${{ github.event.repository.default_branch }}") + pr_number="${{ github.event.inputs.pull_request_number }}" + fi + + echo "check=$check_sha" >> "$GITHUB_OUTPUT" + echo "base=$base_sha" >> "$GITHUB_OUTPUT" + echo "head=$head_sha" >> "$GITHUB_OUTPUT" + if [ -n "$pr_number" ] && [ "$pr_number" != "null" ]; then + echo "pr=$pr_number" >> "$GITHUB_OUTPUT" + fi + + - name: Collect updated pin targets + id: updatedpins + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + base_sha="${{ steps.revision.outputs.base }}" + head_sha="${{ steps.revision.outputs.head }}" + + if [ -z "$base_sha" ] || [ -z "$head_sha" ]; then + echo "has_targets=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + task ci -- collect-updated-pins \ + --base "$base_sha" \ + --head "$head_sha" \ + --workspace "${{ github.workspace }}" \ + --output-json pins-context.json \ + --summary-md pins-summary.md + + if [ -s pins-context.json ]; then + echo "has_targets=true" >> "$GITHUB_OUTPUT" + echo "context=pins-context.json" >> "$GITHUB_OUTPUT" + else + echo "has_targets=false" >> "$GITHUB_OUTPUT" + fi + + - name: Collect new local servers + id: newservers + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + base_sha="${{ steps.revision.outputs.base }}" + head_sha="${{ steps.revision.outputs.head }}" + + if [ -z "$base_sha" ] || [ -z "$head_sha" ]; then + echo "has_targets=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + task ci -- collect-new-servers \ + --base "$base_sha" \ + --head "$head_sha" \ + --workspace "${{ github.workspace }}" \ + --output-json new-servers-context.json \ + --summary-md new-servers-summary.md + + if [ -s new-servers-context.json ]; then + echo "has_targets=true" >> "$GITHUB_OUTPUT" + echo "context=new-servers-context.json" >> "$GITHUB_OUTPUT" + else + echo "has_targets=false" >> "$GITHUB_OUTPUT" + fi + + - name: Switch to main for running reviews + run: | + set -euo pipefail + # We've collected the targets from the PR branch. Now switch to main + # so that all subsequent steps (creating checks, running reviews) use + # the latest security-reviewer implementation from main. + git fetch origin main + git checkout origin/main + + - name: Create pending security review checks + if: steps.updatedpins.outputs.has_targets == 'true' || steps.newservers.outputs.has_targets == 'true' + id: checks + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REVIEW_AGENT_INPUT: ${{ github.event.inputs.agent }} + run: | + set -euo pipefail + + agent="${REVIEW_AGENT_INPUT:-}" + if [ -z "$agent" ]; then + agent="claude" + fi + + check_sha="${{ steps.revision.outputs.check }}" + repo="${{ github.repository }}" + + mkdir -p review-output + + # Store check names and IDs in files for the next step. + > review-output/check-ids.txt + + # Function to create a pending check and return its ID. + create_pending_check() { + local check_name="$1" + local server="$2" + local review_type="$3" + + gh api repos/$repo/check-runs \ + --method POST \ + --field name="$check_name" \ + --field head_sha="$check_sha" \ + --field status="queued" \ + --field output[title]="Security Review: $server" \ + --field output[summary]="Queued for $review_type security review..." \ + --jq '.id' + } + + # Helper to create a slug suitable for check names. + slugify() { + echo "$1" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g' | sed -E 's/^-+|-+$//g' + } + + # Create checks for updated pins (differential reviews). + if [ "${{ steps.updatedpins.outputs.has_targets }}" = "true" ]; then + while read -r target; do + server=$(echo "$target" | jq -r '.server') + project=$(echo "$target" | jq -r '.project') + old_commit=$(echo "$target" | jq -r '.old_commit') + new_commit=$(echo "$target" | jq -r '.new_commit') + + server_slug=$(slugify "$server") + check_name="security-review/$agent/pin/$server_slug" + check_id=$(create_pending_check "$check_name" "$server" "differential") + + echo "$check_name|$check_id|$server|$project|$new_commit|$old_commit|differential" >> review-output/check-ids.txt + done < <(jq -c '.[]' "${{ steps.updatedpins.outputs.context }}") + fi + + # Create checks for new servers (full reviews). + if [ "${{ steps.newservers.outputs.has_targets }}" = "true" ]; then + while read -r target; do + server=$(echo "$target" | jq -r '.server') + project=$(echo "$target" | jq -r '.project') + commit=$(echo "$target" | jq -r '.commit') + + server_slug=$(slugify "$server") + check_name="security-review/$agent/new/$server_slug" + check_id=$(create_pending_check "$check_name" "$server" "full") + + echo "$check_name|$check_id|$server|$project|$commit||full" >> review-output/check-ids.txt + done < <(jq -c '.[]' "${{ steps.newservers.outputs.context }}") + fi + + num_checks=$(wc -l < review-output/check-ids.txt) + echo "Created $num_checks pending check(s)" + echo "has_checks=true" >> "$GITHUB_OUTPUT" + + - name: Run security reviews + if: steps.checks.outputs.has_checks == 'true' + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REVIEW_AGENT_INPUT: ${{ github.event.inputs.agent }} + REVIEW_MODEL_INPUT: ${{ github.event.inputs.model }} + REVIEW_TIMEOUT_INPUT: ${{ github.event.inputs.timeout_secs }} + FORCE_REVIEW_INPUT: ${{ github.event.inputs.force_review }} + run: | + set -euo pipefail + + agent="${REVIEW_AGENT_INPUT:-}" + if [ -z "$agent" ]; then + agent="claude" + fi + + model="${REVIEW_MODEL_INPUT:-}" + timeout_secs="${REVIEW_TIMEOUT_INPUT:-1800}" + force_review="${FORCE_REVIEW_INPUT:-false}" + + check_sha="${{ steps.revision.outputs.check }}" + repo="${{ github.repository }}" + + # Maximum size for GitHub check output text field (in bytes). + max_check_output_size=65000 + + # Function to determine check conclusion from labels. + determine_conclusion() { + local labels_file="$1" + + if [ ! -s "$labels_file" ]; then + echo "success" + elif grep -qE '^security-blocked$' "$labels_file"; then + echo "failure" + elif grep -qE '^security-risk:critical$' "$labels_file"; then + echo "failure" + elif grep -qE '^security-risk:(high|medium)$' "$labels_file"; then + echo "neutral" + else + echo "success" + fi + } + + # Function to check if a review is already completed (unless forced). + should_skip_review() { + local check_name="$1" + + [ "$force_review" != "true" ] && \ + [ -n "$(gh api repos/$repo/commits/$check_sha/check-runs \ + --jq ".check_runs[] | select(.name == \"$check_name\" and .status == \"completed\")" 2>/dev/null)" ] + } + + # Function to mark a check as skipped. + skip_check() { + local check_id="$1" + local server="$2" + local reason="$3" + + gh api repos/$repo/check-runs/$check_id \ + --method PATCH \ + --field status="completed" \ + --field conclusion="skipped" \ + --field output[title]="Security Review: $server" \ + --field output[summary]="Skipped - $reason" + } + + # Function to create a pending check and return its ID. + create_pending_check() { + local check_name="$1" + local server="$2" + local review_type="$3" + + gh api repos/$repo/check-runs \ + --method POST \ + --field name="$check_name" \ + --field head_sha="$check_sha" \ + --field status="queued" \ + --field output[title]="Security Review: $server" \ + --field output[summary]="Queued for $review_type security review..." \ + --jq '.id' + } + + # Function to run a security review and update a check. + run_review() { + local check_name="$1" + local check_id="$2" + local server="$3" + local project="$4" + local head_commit="$5" + local base_commit="$6" + local review_type="$7" + + if [ -z "$project" ] || [ "$project" = "null" ]; then + echo "::warning::Skipping $server: missing project URL" + skip_check "$check_id" "$server" "missing project URL" + return + fi + if [ -z "$head_commit" ] || [ "$head_commit" = "null" ]; then + echo "::warning::Skipping $server: missing head commit" + skip_check "$check_id" "$server" "missing head commit" + return + fi + + # Check if we should skip this review. + if should_skip_review "$check_name"; then + echo "Skipping $server: review already completed for this commit (use force_review to re-run)" >&2 + skip_check "$check_id" "$server" "review already completed for this commit" + return + fi + + echo "Starting $review_type review for $server..." + + # Mark check as in progress. + gh api repos/$repo/check-runs/$check_id \ + --method PATCH \ + --field status="in_progress" \ + --field output[title]="Security Review: $server" \ + --field output[summary]="Running $review_type security review..." + + # Run the security review. + report_path="review-output/${server}-${review_type}.md" + labels_path="review-output/${server}-${review_type}-labels.txt" + + cmd=(task security-reviewer -- \ + --agent "$agent" \ + --repo "$project" \ + --head "$head_commit" \ + --target-label "$server" \ + --output "$report_path" \ + --labels-output "$labels_path" \ + --timeout "$timeout_secs") + + if [ -n "$model" ]; then + cmd+=(--model "$model") + fi + + if [ "$review_type" = "differential" ]; then + if [ -z "$base_commit" ] || [ "$base_commit" = "null" ]; then + echo "::warning::Skipping $server: missing base commit for differential review" + skip_check "$check_id" "$server" "missing base commit" + return + fi + cmd+=(--base "$base_commit") + fi + + if "${cmd[@]}"; then + # Review succeeded - determine conclusion from labels. + conclusion=$(determine_conclusion "$labels_path") + + # Build summary text with beta preamble. + beta_notice=$'**⚠️ Beta Feature:** This automated security review is designed to aid human assessment and may contain spurious findings. Please use your judgment when evaluating the results.\n\n' + + if [ "$review_type" = "differential" ]; then + summary="${beta_notice}Differential review completed (${base_commit:0:7}...${head_commit:0:7})" + else + summary="${beta_notice}Full code review completed at ${head_commit:0:7}" + fi + + # Read labels for summary. + if [ -s "$labels_path" ]; then + labels_list=$(cat "$labels_path" | head -5 | paste -sd, -) + summary=$''"${summary}"$'\nLabels: '"${labels_list}" + fi + + # Read report and truncate if necessary. + if [ -s "$report_path" ]; then + report_text=$(cat "$report_path") + report_size=${#report_text} + + if [ "$report_size" -gt "$max_check_output_size" ]; then + # Truncate and add notice. + truncate_at=$((max_check_output_size - 200)) + report_text="${report_text:0:$truncate_at}" + report_text=$''"${report_text}"$'\n\n---\n\n**Note:** Report truncated due to size limits. Full report available in workflow artifacts.' + fi + else + report_text="No report generated." + fi + + # Update check with results. + gh api repos/$repo/check-runs/$check_id \ + --method PATCH \ + --field status="completed" \ + --field conclusion="$conclusion" \ + --field output[title]="Security Review: $server" \ + --field output[summary]="$summary" \ + --field output[text]="$report_text" + + echo "Completed $review_type review for $server (conclusion: $conclusion)" + else + # Review failed. + gh api repos/$repo/check-runs/$check_id \ + --method PATCH \ + --field status="completed" \ + --field conclusion="failure" \ + --field output[title]="Security Review: $server" \ + --field output[summary]="Security review failed to complete" \ + --field output[text]="The security review process encountered an error. Check workflow logs for details." + + echo "::error::Failed $review_type review for $server" + fi + } + + # Run all reviews using the check IDs created in the previous step. + while IFS='|' read -r check_name check_id server project head_commit base_commit review_type; do + run_review \ + "$check_name" \ + "$check_id" \ + "$server" \ + "$project" \ + "$head_commit" \ + "$base_commit" \ + "$review_type" || true + done < review-output/check-ids.txt + + - name: Upload security reports + if: always() && (steps.updatedpins.outputs.has_targets == 'true' || steps.newservers.outputs.has_targets == 'true') + uses: actions/upload-artifact@v4 + with: + name: security-reports + path: review-output/ + if-no-files-found: warn diff --git a/.github/workflows/security-review-manual.yaml b/.github/workflows/security-review-manual.yaml new file mode 100644 index 00000000..dbaf598f --- /dev/null +++ b/.github/workflows/security-review-manual.yaml @@ -0,0 +1,138 @@ +name: Security Review (Manual) + +on: + workflow_dispatch: + inputs: + servers: + description: "Comma-separated list of local server names to audit (leave blank for all)." + required: false + default: "" + agent: + description: "Optional reviewer agent (claude or codex)." + required: false + default: "" + model: + description: "Optional reviewer model override." + required: false + default: "" + timeout_secs: + description: "Optional reviewer timeout in seconds (defaults to 1800)." + required: false + default: "" + +concurrency: + group: security-review-manual-${{ github.run_id }} + cancel-in-progress: false + +jobs: + full-audit: + name: Execute Full Audit + runs-on: ubuntu-24.04 + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker + uses: docker/setup-docker-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Set up Docker Compose + uses: docker/setup-compose-action@v1 + + - name: Collect audit targets + id: collect + run: | + set -euo pipefail + task ci -- collect-full-audit \ + --workspace "${{ github.workspace }}" \ + --servers "${{ github.event.inputs.servers }}" \ + --output-json audit-targets.json + + if jq -e '. | length > 0' audit-targets.json >/dev/null; then + echo "has_targets=true" >> "$GITHUB_OUTPUT" + echo "targets=audit-targets.json" >> "$GITHUB_OUTPUT" + else + echo "No audit targets identified; exiting." >&2 + echo "has_targets=false" >> "$GITHUB_OUTPUT" + fi + + - name: Run security reviews + if: steps.collect.outputs.has_targets == 'true' + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + REVIEW_AGENT_INPUT: ${{ github.event.inputs.agent }} + REVIEW_MODEL_INPUT: ${{ github.event.inputs.model }} + REVIEW_TIMEOUT_INPUT: ${{ github.event.inputs.timeout_secs }} + run: | + set -uo pipefail + agent="${REVIEW_AGENT_INPUT:-}" + if [ -z "$agent" ]; then + agent="claude" + fi + + model="${REVIEW_MODEL_INPUT:-}" + + timeout_secs="${REVIEW_TIMEOUT_INPUT:-1800}" + + mkdir -p reports + + while read -r target; do + server=$(echo "$target" | jq -r '.server') + project=$(echo "$target" | jq -r '.project') + head_commit=$(echo "$target" | jq -r '.commit') + + if [ -z "$project" ] || [ "$project" = "null" ]; then + echo "::warning::Skipping $server: missing project URL" + continue + fi + if [ -z "$head_commit" ] || [ "$head_commit" = "null" ]; then + echo "::warning::Skipping $server: missing commit information" + continue + fi + + report_path="reports/${server}.md" + labels_path="reports/${server}-labels.txt" + cmd=(task security-reviewer -- \ + --agent "$agent" \ + --repo "$project" \ + --head "$head_commit" \ + --target-label "$server" \ + --output "$report_path" \ + --labels-output "$labels_path" \ + --timeout "$timeout_secs") + + if [ -n "$model" ]; then + cmd+=(--model "$model") + fi + + if ! "${cmd[@]}"; then + echo "::error::Security review failed for $server" + fi + done < <(jq -c '.[]' "${{ steps.collect.outputs.targets }}") + + - name: Upload security reports + if: steps.collect.outputs.has_targets == 'true' + uses: actions/upload-artifact@v4 + with: + name: security-reports + path: reports/ + if-no-files-found: warn diff --git a/.github/workflows/update-pins.yaml b/.github/workflows/update-pins.yaml new file mode 100644 index 00000000..aefc5647 --- /dev/null +++ b/.github/workflows/update-pins.yaml @@ -0,0 +1,222 @@ +name: Update MCP Server Version Pins + +on: + # schedule: + # - cron: "0 5 * * *" + workflow_dispatch: + inputs: + max_new_prs: + description: "Maximum number of new pull requests to create (leave blank for unlimited)." + required: false + default: "" + servers: + description: "Comma-separated list of servers to update (leave blank for all)." + required: false + default: "" + +concurrency: + group: update-pins + cancel-in-progress: false + +permissions: + contents: write + pull-requests: write + +jobs: + update-pins: + runs-on: ubuntu-24.04 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Configure Git user + run: | + git config user.name "docker-mcp-bot" + git config user.email "docker-mcp-bot@users.noreply.github.com" + + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Update pinned commits + env: + GITHUB_TOKEN: ${{ secrets.UPDATE_PINS_GITHUB_TOKEN }} + run: | + if [ -n "${{ github.event.inputs.servers }}" ]; then + task ci -- update-pins --servers "${{ github.event.inputs.servers }}" + else + task ci -- update-pins + fi + + - name: Collect per-server patches + id: prepare + env: + ALLOWED_SERVERS: ${{ github.event.inputs.servers || '' }} + run: | + # Gather the diff for each modified server YAML and store it as an + # individual patch file so we can open one PR per server. + mkdir -p patches + changed_files=$(git status --porcelain | awk '$2 ~ /^servers\/.*\/server.yaml$/ {print $2}') + if [ -z "$changed_files" ]; then + echo "changed=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + allowed_servers=$(echo "$ALLOWED_SERVERS" | tr '[:upper:]' '[:lower:]' | tr -d ' ') + server_list=() + for file in $changed_files; do + server=$(basename "$(dirname "$file")") + server_lc=$(echo "$server" | tr '[:upper:]' '[:lower:]') + + if [ -n "$allowed_servers" ]; then + if ! echo ",$allowed_servers," | grep -q ",$server_lc,"; then + continue + fi + fi + + git diff -- "$file" > "patches/${server}.patch" + server_list+=("$server") + done + + if [ ${#server_list[@]} -eq 0 ]; then + echo "No servers matched the provided filter; exiting." >&2 + git checkout -- servers + echo "changed=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Reset the working tree so we can apply patches one-at-a-time. + git checkout -- servers + + # Expose the server list to later steps. + printf '%s\n' "${server_list[@]}" | paste -sd',' - > patches/servers.txt + echo "changed=true" >> "$GITHUB_OUTPUT" + echo "servers=$(cat patches/servers.txt)" >> "$GITHUB_OUTPUT" + + - name: Create or update pull requests + if: steps.prepare.outputs.changed == 'true' + env: + GH_TOKEN: ${{ secrets.UPDATE_PINS_GITHUB_TOKEN }} + MAX_NEW_PRS: ${{ github.event.inputs.max_new_prs || '' }} + run: | + IFS=',' read -ra SERVERS <<< "${{ steps.prepare.outputs.servers }}" + new_pr_limit=$(echo "$MAX_NEW_PRS" | tr -d ' ') + if [ -n "$new_pr_limit" ] && ! [[ "$new_pr_limit" =~ ^[0-9]+$ ]]; then + echo "Invalid max_new_prs value: $new_pr_limit" >&2 + exit 1 + fi + new_pr_count=0 + failed_servers=() + + for server in "${SERVERS[@]}"; do + patch="patches/${server}.patch" + if [ ! -s "$patch" ]; then + echo "No patch found for $server, skipping." + continue + fi + + # Look up the new commit hash in the patch so we can decide whether + # an existing automation branch already covers it. + new_commit=$(awk '/^\+.*commit:/{print $2}' "$patch" | tail -n1) + branch="automation/update-pin-${server}" + + # Check if a PR already exists for this branch. + existing_pr=$(gh pr list --head "$branch" --json number --jq '.[0].number // ""' 2>/dev/null || echo "") + + # Check quota before doing any git work if no PR exists. + if [ -z "$existing_pr" ] && [ -n "$new_pr_limit" ] && [ "$new_pr_count" -ge "$new_pr_limit" ]; then + echo "New PR quota reached ($new_pr_limit); skipping $server." + continue + fi + + # Check if branch exists and whether we need to update it. + branch_exists=false + if git ls-remote --exit-code --heads origin "$branch" >/dev/null 2>&1; then + branch_exists=true + # Check if existing branch already has the commit we want. + git fetch origin "$branch" + existing_commit=$(git show "origin/${branch}:servers/${server}/server.yaml" 2>/dev/null | awk '/commit:/{print $2}' | tail -n1) + if [ -n "$existing_commit" ] && [ "$existing_commit" = "$new_commit" ]; then + # Only skip if there's also an open PR for this branch. + if [ -n "$existing_pr" ]; then + echo "Existing PR #$existing_pr for $server already pins ${existing_commit}; skipping." + continue + else + echo "Branch for $server already has ${existing_commit}, but no open PR found; will recreate." + fi + fi + fi + + # Start from a clean copy of main for each server so branches do not + # interfere with one another. + git checkout main + git fetch origin main + git reset --hard origin/main + + # Apply the patch onto a fresh branch for this server. + git checkout -B "$branch" origin/main + if ! git apply "$patch"; then + echo "::error::Failed to apply patch for $server, skipping." + continue + fi + + if git diff --quiet; then + echo "No changes after applying patch for $server, skipping." + continue + fi + + # Commit the server YAML change and force-push the automation branch. + git add "servers/${server}/server.yaml" + git commit -m "chore: update pin for ${server}" + if ! git push --force origin "$branch"; then + echo "::error::Failed to push branch for $server, skipping." + continue + fi + + # Create or update the PR dedicated to this server. + if [ -n "$existing_pr" ]; then + # Update existing PR. + if ! gh pr edit "$existing_pr" \ + --title "chore: update pin for ${server}" \ + --body "Automated commit pin update for ${server}." 2>&1; then + echo "::error::Failed to update PR for $server" + failed_servers+=("$server (update)") + else + echo "Updated existing PR #$existing_pr for $server" + fi + else + # Create new PR. + if gh pr create \ + --title "chore: update pin for ${server}" \ + --body "Automated commit pin update for ${server}." \ + --base main \ + --head "$branch" 2>&1; then + new_pr_count=$((new_pr_count + 1)) + echo "Created new PR for $server" + else + echo "::error::Failed to create PR for $server" + failed_servers+=("$server (create)") + fi + fi + done + + # Leave the repository in a clean state. + git checkout main + + # Report summary and exit with error if any PRs failed. + if [ ${#failed_servers[@]} -gt 0 ]; then + echo "::error::Failed to create or update PRs for ${#failed_servers[@]} server(s):" + for server in "${failed_servers[@]}"; do + echo " - $server" + done + exit 1 + fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 635b8ad0..d1531aed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,6 +3,26 @@ Thank you for your interest in contributing to the official Docker MCP Registry. This document outlines how to contribute to this project. +## 📦 Types of MCP Servers + +There are two types of MCP servers you can add to the registry: + +### 🏠 Local Servers (Containerized) +Local servers run in Docker containers on your machine. They: +- Require a Dockerfile in the source repository +- Are built and hosted as Docker images +- Run locally with full container isolation +- Can benefit from Docker-built images with enhanced security features (signatures, provenance, SBOMs, automatic updates) + +### 🌐 Remote Servers (Hosted) +Remote servers are hosted externally and accessed via HTTP(S). They: +- Don't require a Dockerfile (already deployed somewhere) +- Use `streamable-http` or `sse` transport protocols +- Often require OAuth authentication +- Have dynamic tool discovery + +**If you're adding a remote server,** skip to the [Adding a Remote MCP Server](#adding-a-remote-mcp-server) section below. + ## Prerequisites - Go v1.24+ @@ -20,7 +40,7 @@ This document outlines how to contribute to this project. - Every pull request requires a review from the Docker team before merging. - Once approved, all of your commits will be squashed into a single commit with your PR title. -## 📋 Step-by-Step Guide +## 🏠 Adding a Local MCP Server ### 1️⃣ Fork this repository @@ -64,6 +84,7 @@ about: icon: https://avatars.githubusercontent.com/u/182288589?s=200&v=4 source: project: https://github.com/myorg/my-orgdb-mcp + commit: 0123456789abcdef0123456789abcdef01234567 config: description: Configure the connection to TODO secrets: @@ -179,6 +200,142 @@ Upon approval your entry will be processed and it will be available in 24 hours - [Docker Desktop's MCP Toolkit](https://www.docker.com/products/docker-desktop/) - [Docker Hub `mcp` namespace](https://hub.docker.com/u/mcp) (for MCP servers built by Docker) +--- + +## 🌐 Adding a Remote MCP Server + +Remote MCP servers are already hosted externally and don't require Docker image building. They communicate via HTTP(S) protocols. + +### Prerequisites for Remote Servers + +- A publicly accessible MCP server endpoint (e.g., `https://mcp.example.com/mcp`) +- Knowledge of the transport protocol (`streamable-http` or `sse`) +- A documentation URL for your server +- OAuth configuration details (if authentication is required) + +### 1️⃣ Fork this repository + +Fork the repository to your own GitHub account and clone it locally. + +#### 2️⃣ Create your remote server entry using `task remote-wizard` + +The easiest way to create a remote server configuration is using the wizard: + +```bash +task remote-wizard +``` + +The wizard will guide you through: +1. **Basic Information**: Server name and category +2. **Server Details**: Title, description, icon URL, and documentation URL +3. **Remote Configuration**: Transport type (streamable-http or sse) and server URL +4. **OAuth Configuration**: Simple yes/no question + +If OAuth is enabled, the wizard automatically generates: +- **Provider**: Uses your server name (e.g., `linear`) +- **Secret**: `{server-name}.personal_access_token` (e.g., `linear.personal_access_token`) +- **Environment Variable**: `{SERVER_NAME}_PERSONAL_ACCESS_TOKEN` (e.g., `LINEAR_PERSONAL_ACCESS_TOKEN`) + +This will create a directory under `servers/` with three files: +- `server.yaml` - Server configuration +- `tools.json` - Empty array (for dynamic tool discovery) +- `readme.md` - Documentation link + +#### 3️⃣ Review the generated files + +The wizard has created all necessary files for you. The `tools.json` file is always an empty array `[]` for remote servers because they use dynamic tool discovery. The `readme.md` file contains your documentation link. + +#### 4️⃣ Example remote server structure + +Your remote server directory should look like this: + +``` +servers/my-remote-server/ +├── server.yaml # Server configuration +├── tools.json # Always [] for remote servers +└── readme.md # Documentation link (required) +``` + +Example `server.yaml` for a remote server **with OAuth** (like `servers/linear`): + +```yaml +name: linear +type: remote +dynamic: + tools: true +meta: + category: productivity + tags: + - productivity + - project-management + - remote +about: + title: Linear + description: Track issues and plan sprints + icon: https://www.google.com/s2/favicons?domain=linear.app&sz=64 +remote: + transport_type: streamable-http + url: https://mcp.linear.app/mcp +oauth: + - provider: linear + secret: linear.personal_access_token + env: LINEAR_PERSONAL_ACCESS_TOKEN +``` + +Example `server.yaml` for a remote server **without OAuth** (like `servers/cloudflare-docs`): + +```yaml +name: cloudflare-docs +type: remote +meta: + category: documentation + tags: + - documentation + - cloudflare + - remote +about: + title: Cloudflare Docs + description: Access the latest documentation on Cloudflare products + icon: https://www.cloudflare.com/favicon.ico +remote: + transport_type: sse + url: https://docs.mcp.cloudflare.com/sse +``` + +**Note:** Remote servers without OAuth don't need the `oauth` field or `dynamic.tools` field in their configuration. + +#### 5️⃣ Test your remote server locally + +You can test your remote server configuration by importing it into Docker Desktop: + +```bash +task catalog -- my-remote-server +docker mcp catalog import $PWD/catalogs/my-remote-server/catalog.yaml +docker mcp server enable my-remote-server +``` + +For OAuth-enabled servers, authorize the server: + +```bash +docker mcp oauth authorize my-remote-server +``` + +Now you can start the gateway with `docker mcp gateway run` and test tool calls to the remote server. + +When done testing, reset the catalog: + +```bash +docker mcp catalog reset +``` + +#### 6️⃣ Open a pull request + +Create a pull request with your remote server files. Make sure to: +- Include all required files (`server.yaml`, `tools.json`, and `readme.md`) +- Verify that your server URL is publicly accessible +- Test OAuth configuration if applicable +- Ensure the documentation URL in `readme.md` is valid + ## 📜 Code of Conduct This project follows a Code of Conduct. Please review it in diff --git a/Taskfile.yml b/Taskfile.yml index 41f06de7..50d35143 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -4,7 +4,7 @@ tasks: create: desc: Create a new mcp server definition cmd: go run ./cmd/create {{.CLI_ARGS}} - + build: desc: Build a server image cmd: go run ./cmd/build {{.CLI_ARGS}} @@ -12,21 +12,41 @@ tasks: catalog: desc: Generate a test catalog cmd: go run ./cmd/catalog {{.CLI_ARGS}} - + wizard: desc: Run the wizard cmd: go run ./cmd/wizard {{.CLI_ARGS}} - + + remote-wizard: + desc: Run the remote server wizard + cmd: go run ./cmd/remote-wizard {{.CLI_ARGS}} + validate: desc: Validate a server cmd: go run ./cmd/validate {{.CLI_ARGS}} - + + clean: + desc: Clean build artifacts for servers + cmd: go run ./cmd/clean {{.CLI_ARGS}} + + ci: + desc: Run CI helper utilities + cmd: go run ./cmd/ci {{.CLI_ARGS}} + import: desc: Import a server into the registry cmd: docker mcp catalog import ./catalogs/{{.CLI_ARGS}}/catalog.yaml - + reset: desc: Reset the catalog cmds: - docker mcp catalog reset - - docker mcp catalog init \ No newline at end of file + - docker mcp catalog init + + unittest: + desc: Run Go unit tests + cmd: go test ./... + + security-reviewer: + desc: Run the security reviewer agent + cmd: go run ./cmd/security-reviewer {{.CLI_ARGS}} diff --git a/agents/security-reviewer/Dockerfile b/agents/security-reviewer/Dockerfile new file mode 100644 index 00000000..8cbe29da --- /dev/null +++ b/agents/security-reviewer/Dockerfile @@ -0,0 +1,92 @@ +# syntax=docker/dockerfile:1.7 + +# Use a golang image to build the reviewer and proxy binaries (with cgo disabled). +FROM golang:1.25-trixie AS gobuilder +ENV CGO_ENABLED=0 + +# Create and set a working directory for the code. +RUN mkdir -p /security-reviewer +WORKDIR /security-reviewer + +# Copy dependency specifications and ensure they're downloaded. +COPY go.mod go.sum ./ +RUN go mod download + +# Copy reviewer and proxy sources, then build both binaries. +COPY reviewer ./reviewer +COPY proxy ./proxy +RUN mkdir -p /security-reviewer/build +RUN go build -o /security-reviewer/build/reviewer ./reviewer +RUN go build -o /security-reviewer/build/proxy ./proxy + + +# Use a debian image to host the agent. +FROM debian:trixie-slim AS reviewer-image + +# Set up configuration. +ARG CLAUDE_CODE_VERSION=latest +ARG CODEX_CLI_VERSION=latest +ARG AGENT_UID=1000 +ARG AGENT_GID=1000 + +# Install core packages, developer tooling, and runtime dependencies. +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + git \ + gnupg \ + jq \ + nodejs \ + npm \ + ripgrep \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +# Install Claude Code and Codex CLIs for headless operation. +RUN npm install --global \ + @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION} \ + @openai/codex@${CODEX_CLI_VERSION} \ + && npm cache clean --force + +# Create a dedicated runtime user and working directories. +# Use build args to allow matching host UID/GID to avoid permission issues. +RUN groupadd --gid ${AGENT_GID} agent \ + && useradd --uid ${AGENT_UID} --gid ${AGENT_GID} --create-home --shell /bin/bash agent \ + && install -d -o agent -g agent /workspace \ + && install -d -o agent -g agent /workspace/input \ + && install -d -o agent -g agent /workspace/output + +# Copy the reviewer entrypoint and assets. +COPY --from=gobuilder /security-reviewer/build/reviewer /opt/security-reviewer/reviewer +COPY --chown=agent:agent prompt-template.md /opt/security-reviewer/prompt-template.md +COPY --chown=agent:agent report-template.md /opt/security-reviewer/report-template.md + +# Set the reviewer user and working directory. +USER agent +WORKDIR /workspace + +# Set the reviewer entrypoint. +ENTRYPOINT ["/opt/security-reviewer/reviewer"] + + +# Use a debian image to host the proxy. +FROM debian:trixie-slim AS proxy-image + +# Install dependencies, including those needed for healthchecks. +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + wget \ + && rm -rf /var/lib/apt/lists/* + +# Copy the proxy entrypoint. +COPY --from=gobuilder /security-reviewer/build/proxy /opt/security-reviewer/proxy + +# Expose the proxy's default port. +EXPOSE 4000 + +# Set the proxy entrypoint. +ENTRYPOINT ["/opt/security-reviewer/proxy"] diff --git a/agents/security-reviewer/compose.yml b/agents/security-reviewer/compose.yml new file mode 100644 index 00000000..73f1eced --- /dev/null +++ b/agents/security-reviewer/compose.yml @@ -0,0 +1,75 @@ +services: + reviewer: + build: + context: . + target: reviewer-image + args: + AGENT_UID: ${AGENT_UID:-1000} + AGENT_GID: ${AGENT_GID:-1000} + image: security-reviewer:latest + depends_on: + proxy: + condition: service_healthy + environment: + # Configure the reviewer CLIs to authenticate against the local proxy. + # Note: We want Claude Code to use a bearer token when making API requests + # to the proxy, not an X-Api-Key header. If we set ANTHROPIC_API_KEY, it + # will use an X-Api-Key header; ANTHROPIC_AUTH_TOKEN uses a bearer token. + ANTHROPIC_AUTH_TOKEN: "sk-compose-clients" + ANTHROPIC_BASE_URL: "http://proxy:4000/anthropic" + CLAUDE_REVIEW_MODEL: ${CLAUDE_REVIEW_MODEL:-claude-sonnet-4-5-20250929} + # Note: Codex won't respect the OPENAI_API_KEY environment variable when + # running in non-interactive mode, it will only use the CODEX_API_KEY + # environment variable. + CODEX_API_KEY: "sk-compose-clients" + OPENAI_BASE_URL: "http://proxy:4000/openai" + CODEX_REVIEW_MODEL: ${CODEX_REVIEW_MODEL:-gpt-5-codex} + REVIEW_AGENT: ${REVIEW_AGENT:-claude} + REVIEW_HEAD_SHA: ${REVIEW_HEAD_SHA:-} + REVIEW_BASE_SHA: ${REVIEW_BASE_SHA:-} + REVIEW_TARGET_LABEL: ${REVIEW_TARGET_LABEL:-} + REVIEW_AGENT_EXTRA_ARGS: ${REVIEW_AGENT_EXTRA_ARGS:-} + REVIEW_TIMEOUT_SECS: ${REVIEW_TIMEOUT_SECS:-3600} + volumes: + - type: bind + source: ${REVIEW_REPOSITORY_PATH:?Set REVIEW_REPOSITORY_PATH to bind the repository under review} + target: /workspace/input/repository + read_only: true + - type: bind + source: ${REVIEW_OUTPUT_PATH:?Set REVIEW_OUTPUT_PATH for report output} + target: /workspace/output + networks: + - internal + + proxy: + build: + context: . + target: proxy-image + image: security-reviewer-proxy:latest + environment: + PROXY_LISTEN_ADDR: ":4000" + PROXY_OPENAI_BASE_URL: ${PROXY_OPENAI_BASE_URL:-https://api.openai.com/v1} + # NOTE: Anthropic clients such as Claude Code will implicitly include a + # /v1 component in their request URLs, so if you override this environment + # variable, be sure not to include a /v1 path component. + PROXY_ANTHROPIC_BASE_URL: ${PROXY_ANTHROPIC_BASE_URL:-https://api.anthropic.com} + PROXY_API_KEY: "sk-compose-clients" + OPENAI_API_KEY: ${OPENAI_API_KEY:-} + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 http://localhost:4000/health/liveness || exit 1"] + interval: 5s + timeout: 5s + retries: 5 + start_period: 5s + networks: + - internal + - external + restart: unless-stopped + +networks: + internal: + driver: bridge + internal: true + external: + driver: bridge diff --git a/agents/security-reviewer/go.mod b/agents/security-reviewer/go.mod new file mode 100644 index 00000000..f48fd52a --- /dev/null +++ b/agents/security-reviewer/go.mod @@ -0,0 +1,5 @@ +module github.com/docker/mcp-registry/agents/security-reviewer + +go 1.25.3 + +require github.com/mattn/go-shellwords v1.0.12 diff --git a/agents/security-reviewer/go.sum b/agents/security-reviewer/go.sum new file mode 100644 index 00000000..da8b56c3 --- /dev/null +++ b/agents/security-reviewer/go.sum @@ -0,0 +1,2 @@ +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= diff --git a/agents/security-reviewer/prompt-template.md b/agents/security-reviewer/prompt-template.md new file mode 100644 index 00000000..ee46660b --- /dev/null +++ b/agents/security-reviewer/prompt-template.md @@ -0,0 +1,44 @@ +# Docker MCP Security Review Instructions + +$MODE_SUMMARY + +Security review metadata: +- Mode: $MODE_LABEL +- Repository name: $TARGET_LABEL +- Repository path: $REPOSITORY_PATH +- Head commit: $HEAD_COMMIT +- Base commit: $BASE_COMMIT +- Commit range: $COMMIT_RANGE + +Core analysis directives: +- $GIT_DIFF_HINT +- Hunt aggressively for intentionally malicious behavior (exfiltration, + persistence, destructive payloads) in addition to accidental security bugs. +- Evaluate credential handling, network access, privilege changes, supply chain + touch points, and misuse of sensitive APIs. +- Use only the tools provided (git, ripgrep, jq, etc.); outbound network access + is unavailable. +- Keep any files you create within /workspace or $REPOSITORY_PATH. + +Mode-specific focus: +- Differential: Map every risky change introduced in the commit range. Call out + suspicious files, shell commands, or configuration shifts. Note beneficial + security hardening too. +- Full: Examine the entire repository snapshot. Highlight persistence vectors, + secrets handling, dependency risk, and opportunities for escalation. + +Report expectations: +- Reproduce every heading, section order, and field exactly as written in + `$REPORT_TEMPLATE_PATH`; replace bracketed placeholders with concrete + content but do not add or remove sections. +- Save the final report to $REPORT_PATH. +- Articulate severity, impact, evidence, and remediation for each issue. + +Labeling guidance: +- Write labels to $LABELS_PATH, one per line. +- Emit exactly one overall risk label in the form `security-risk:` where + `` is one of `critical`, `high`, `medium`, `low`, or `info`. +- Align the chosen label with the overall risk level declared in the report. +- If you identify blocking or critical issues that must halt release, also + include the label `security-blocked` on a separate line. +- Leave $LABELS_PATH empty only if the review cannot be completed. diff --git a/agents/security-reviewer/proxy/main.go b/agents/security-reviewer/proxy/main.go new file mode 100644 index 00000000..a1efe642 --- /dev/null +++ b/agents/security-reviewer/proxy/main.go @@ -0,0 +1,337 @@ +package main + +import ( + "context" + "crypto/subtle" + "errors" + "fmt" + "log" + "net" + "net/http" + "net/http/httputil" + "net/url" + "os" + "os/signal" + "strings" + "syscall" + "time" +) + +const ( + // defaultListenAddr is the fallback bind address when none is provided via PROXY_LISTEN_ADDR. + defaultListenAddr = ":4000" + // defaultOpenAIBaseURL is the upstream OpenAI API base path used when none is provided. + defaultOpenAIBaseURL = "https://api.openai.com/v1" + // defaultAnthropicBaseURL is the upstream Anthropic API base path used when none is provided. + // NOTE: Anthropic clients are expected to include /v1 in their requests, so + // it is not idiomatic to include it in the base URL. + defaultAnthropicBaseURL = "https://api.anthropic.com/" + // openAIInboundPrefix is the path prefix used to route requests to OpenAI. + openAIInboundPrefix = "/openai/" + // anthropicInboundPrefix is the path prefix used to route requests to Anthropic. + anthropicInboundPrefix = "/anthropic/" + // healthPath is the HTTP endpoint used for container health checks. + healthPath = "/health/liveness" + // headerAuthorization is the inbound HTTP header that carries bearer tokens. + headerAuthorization = "Authorization" + // headerAnthropicAPIKey is the Anthropic-specific header carrying API keys. + headerAnthropicAPIKey = "X-Api-Key" +) + +// providerProxy defines how to forward requests to a specific upstream API. +type providerProxy struct { + // Prefix is the inbound path prefix handled by the provider. + Prefix string + // Target is the upstream endpoint used to service requests for the provider. + Target *url.URL + // HeaderName is the outbound header carrying the provider-specific credential. + HeaderName string + // HeaderValue is the credential value set on outbound requests. + HeaderValue string + // DisplayName is the human-readable name of the provider used in logs. + DisplayName string +} + +// main configures the proxy service and starts the HTTP server. +func main() { + cfg, err := loadConfig() + if err != nil { + log.Fatalf("proxy configuration error: %v", err) + } + + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + defer stop() + + mux := http.NewServeMux() + mux.HandleFunc(healthPath, handleHealth) + + mountProxy(mux, cfg.openAIProxy, cfg.clientToken) + mountProxy(mux, cfg.anthropicProxy, cfg.clientToken) + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + http.NotFound(w, r) + }) + + server := &http.Server{ + Addr: cfg.listenAddr, + Handler: withLogging(mux), + ReadTimeout: 15 * time.Second, + // WriteTimeout needs to be relatively high because it limits how long + // the upstream inference API has to respond. + WriteTimeout: 3600 * time.Second, + IdleTimeout: 60 * time.Second, + } + + log.Printf("proxy listening on %s (OpenAI -> %s, Anthropic -> %s)", + cfg.listenAddr, cfg.openAIProxy.Target.String(), cfg.anthropicProxy.Target.String()) + + go func() { + <-ctx.Done() + shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := server.Shutdown(shutdownCtx); err != nil { + log.Printf("proxy shutdown error: %v", err) + } + }() + + if err = server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + log.Fatalf("proxy server error: %v", err) + } +} + +// proxyConfig captures runtime settings for the reverse proxy. +type proxyConfig struct { + listenAddr string + openAIProxy providerProxy + anthropicProxy providerProxy + clientToken string +} + +// loadConfig reads environment variables and constructs the proxy configuration. +func loadConfig() (proxyConfig, error) { + listen := firstNonEmpty(os.Getenv("PROXY_LISTEN_ADDR"), defaultListenAddr) + + clientToken := strings.TrimSpace(os.Getenv("PROXY_API_KEY")) + if clientToken == "" { + return proxyConfig{}, errors.New("PROXY_API_KEY must be set") + } + + openAIBase, err := parseBaseURL(firstNonEmpty(os.Getenv("PROXY_OPENAI_BASE_URL"), defaultOpenAIBaseURL)) + if err != nil { + return proxyConfig{}, fmt.Errorf("parse OpenAI base URL: %w", err) + } + anthropicBase, err := parseBaseURL(firstNonEmpty(os.Getenv("PROXY_ANTHROPIC_BASE_URL"), defaultAnthropicBaseURL)) + if err != nil { + return proxyConfig{}, fmt.Errorf("parse Anthropic base URL: %w", err) + } + + openAIKey := strings.TrimSpace(os.Getenv("OPENAI_API_KEY")) + anthropicKey := strings.TrimSpace(os.Getenv("ANTHROPIC_API_KEY")) + + openAIProxy := providerProxy{ + Prefix: openAIInboundPrefix, + Target: openAIBase, + HeaderName: headerAuthorization, + HeaderValue: bearerValue(openAIKey), + DisplayName: "OpenAI", + } + anthropicProxy := providerProxy{ + Prefix: anthropicInboundPrefix, + Target: anthropicBase, + HeaderName: headerAnthropicAPIKey, + HeaderValue: anthropicKey, + DisplayName: "Anthropic", + } + + return proxyConfig{ + listenAddr: listen, + openAIProxy: openAIProxy, + anthropicProxy: anthropicProxy, + clientToken: clientToken, + }, nil +} + +// mountProxy attaches a provider proxy to the HTTP mux. +func mountProxy(mux *http.ServeMux, provider providerProxy, clientToken string) { + handler := buildProviderHandler(provider, clientToken) + mux.Handle(provider.Prefix, handler) +} + +// buildProviderHandler creates an HTTP handler that forwards requests to the provider. +func buildProviderHandler(provider providerProxy, clientToken string) http.Handler { + proxy := httputil.NewSingleHostReverseProxy(provider.Target) + originalDirector := proxy.Director + proxy.Director = func(req *http.Request) { + inboundPath := req.URL.Path + inboundRawPath := req.URL.RawPath + originalDirector(req) + rewriteRequest(req, inboundPath, inboundRawPath, provider) + } + proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) { + log.Printf("proxy error [%s]: %v", provider.DisplayName, err) + http.Error(w, "upstream request failed", http.StatusBadGateway) + } + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !strings.HasPrefix(r.URL.Path, provider.Prefix) { + http.NotFound(w, r) + return + } + if provider.HeaderValue == "" { + log.Printf("proxy warning [%s]: request rejected due to missing API key", provider.DisplayName) + http.Error(w, "upstream API key is not configured", http.StatusServiceUnavailable) + return + } + if !validateClientToken(r.Header.Get(headerAuthorization), clientToken) { + log.Printf("proxy warning [%s]: request rejected due to missing or invalid client bearer token", provider.DisplayName) + http.Error(w, "invalid bearer token", http.StatusUnauthorized) + return + } + + proxy.ServeHTTP(w, r) + }) +} + +// rewriteRequest adjusts the outbound request before it is sent upstream. +func rewriteRequest(req *http.Request, inboundPath, inboundRawPath string, provider providerProxy) { + req.URL.Scheme = provider.Target.Scheme + req.URL.Host = provider.Target.Host + req.Host = provider.Target.Host + + trimmedPath := strings.TrimPrefix(inboundPath, provider.Prefix) + if trimmedPath == inboundPath { + trimmedPath = "" + } + + basePath := provider.Target.Path + extraPath := singleLeadingSlash(trimmedPath) + req.URL.Path = joinURLPath(basePath, extraPath) + + trimmedRaw := "" + if inboundRawPath != "" { + trimmedRaw = strings.TrimPrefix(inboundRawPath, provider.Prefix) + if trimmedRaw == inboundRawPath { + trimmedRaw = "" + } + } + if trimmedRaw != "" { + req.URL.RawPath = joinURLPath(basePath, singleLeadingSlash(trimmedRaw)) + } else { + req.URL.RawPath = req.URL.Path + } + + stripSensitiveHeaders(req.Header) + + if provider.HeaderName == headerAuthorization { + req.Header.Set(headerAuthorization, provider.HeaderValue) + } else if provider.HeaderName != "" { + req.Header.Set(provider.HeaderName, provider.HeaderValue) + } +} + +// stripSensitiveHeaders removes inbound authentication headers that should not propagate upstream. +func stripSensitiveHeaders(header http.Header) { + header.Del(headerAuthorization) + header.Del(headerAnthropicAPIKey) +} + +// joinURLPath concatenates base and additional path segments. +func joinURLPath(basePath, extraPath string) string { + switch { + case basePath == "" || basePath == "/": + return singleLeadingSlash(extraPath) + case extraPath == "" || extraPath == "/": + return singleLeadingSlash(basePath) + default: + return singleLeadingSlash(strings.TrimSuffix(basePath, "/") + "/" + strings.TrimPrefix(extraPath, "/")) + } +} + +// singleLeadingSlash ensures the provided path has a leading slash. +func singleLeadingSlash(path string) string { + if path == "" { + return "/" + } + if !strings.HasPrefix(path, "/") { + return "/" + path + } + return path +} + +// withLogging wraps the handler with structured request logging. +func withLogging(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + next.ServeHTTP(w, r) + duration := time.Since(start) + remote := remoteAddr(r.Context(), r.RemoteAddr) + if r.URL.Path != healthPath { + log.Printf("proxy request method=%s path=%s remote=%s duration=%s", + r.Method, r.URL.Path, remote, duration) + } + }) +} + +// remoteAddr normalizes the remote address for logging. +func remoteAddr(ctx context.Context, fallback string) string { + if peer, ok := ctx.Value(http.LocalAddrContextKey).(net.Addr); ok { + return peer.String() + } + return fallback +} + +// handleHealth responds to health check requests. +func handleHealth(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + _, _ = w.Write([]byte("ok")) +} + +// parseBaseURL validates and normalizes the upstream base URL. +func parseBaseURL(raw string) (*url.URL, error) { + parsed, err := url.Parse(raw) + if err != nil { + return nil, err + } + if parsed.Scheme == "" || parsed.Host == "" { + return nil, fmt.Errorf("invalid URL %q (must include scheme and host)", raw) + } + if !strings.HasSuffix(parsed.Path, "/") { + parsed.Path += "/" + } + return parsed, nil +} + +// bearerValue formats the bearer token header. +func bearerValue(key string) string { + key = strings.TrimSpace(key) + if key == "" { + return "" + } + return "Bearer " + key +} + +// firstNonEmpty returns the first non-empty string in candidates. +func firstNonEmpty(values ...string) string { + for _, v := range values { + if strings.TrimSpace(v) != "" { + return v + } + } + return "" +} + +// validateClientToken ensures inbound requests present the proxy bearer secret using +// a constant-time comparison to avoid leaking timing information. +func validateClientToken(headerValue, expectedToken string) bool { + if expectedToken == "" { + return false + } + parts := strings.SplitN(headerValue, " ", 2) + if len(parts) != 2 { + return false + } + if !strings.EqualFold(parts[0], "bearer") { + return false + } + provided := strings.TrimSpace(parts[1]) + return subtle.ConstantTimeCompare([]byte(provided), []byte(expectedToken)) == 1 +} diff --git a/agents/security-reviewer/report-template.md b/agents/security-reviewer/report-template.md new file mode 100644 index 00000000..f2be42fe --- /dev/null +++ b/agents/security-reviewer/report-template.md @@ -0,0 +1,37 @@ +# Security Review Report + +## Scope Summary +- **Review Mode:** [Differential/Full] +- **Repository:** [URL or path] +- **Head Commit:** [SHA] +- **Base Commit:** [SHA or N/A] +- **Commit Range:** [base...head or single commit] +- **Overall Risk Level:** [CRITICAL/HIGH/MEDIUM/LOW/INFO] + +## Executive Summary +[One to two paragraphs summarizing the repository's security posture.] + +## Detailed Findings +### [Finding title — severity] +- **Impact:** [Describe the risk to users or systems.] +- **Evidence:** [Point to files, commits, or code snippets.] +- **Recommendation:** [Explain how to mitigate or remediate.] + +### [Finding title — severity] +- **Impact:** [...] +- **Evidence:** [...] +- **Recommendation:** [...] + +## Defense-In-Depth Observations +- [Optional notes about positive security patterns or remaining gaps.] + +## Recommended Labels +- [Label name — justification] +- [Label name — justification] + +## Next Steps +- [Follow-up task or owner] +- [Timeline or priority guidance] + +## Conclusion +[Final remarks and call-outs for reviewers.] diff --git a/agents/security-reviewer/reviewer/agent.go b/agents/security-reviewer/reviewer/agent.go new file mode 100644 index 00000000..77ccd611 --- /dev/null +++ b/agents/security-reviewer/reviewer/agent.go @@ -0,0 +1,35 @@ +package main + +import ( + "context" + "fmt" + "os/exec" +) + +const ( + // agentNameClaude identifies the Claude Code based reviewer. + agentNameClaude = "claude" + // agentNameCodex identifies the Codex based reviewer. + agentNameCodex = "codex" +) + +// reviewerAgent defines the behavior required by each agent implementation. +type reviewerAgent interface { + Name() string + // ModelEnvVar returns the environment variable that overrides the agent's model, or empty when not applicable. + ModelEnvVar() string + // BuildCommand returns the configured command used to invoke the agent. + BuildCommand(ctx context.Context, inv agentInvocation) (*exec.Cmd, error) +} + +// selectAgent resolves an agent by name. +func selectAgent(name string) (reviewerAgent, error) { + switch name { + case agentNameClaude: + return claudeAgent{}, nil + case agentNameCodex: + return codexAgent{}, nil + default: + return nil, fmt.Errorf("unsupported review agent: %s", name) + } +} diff --git a/agents/security-reviewer/reviewer/claude.go b/agents/security-reviewer/reviewer/claude.go new file mode 100644 index 00000000..2b4a66da --- /dev/null +++ b/agents/security-reviewer/reviewer/claude.go @@ -0,0 +1,59 @@ +package main + +import ( + "context" + "fmt" + "os/exec" + "strings" + + "github.com/mattn/go-shellwords" +) + +// claudeAgent implements reviewerAgent for Claude Code. +type claudeAgent struct{} + +// Name returns the stable identifier for the Claude agent implementation. +func (claudeAgent) Name() string { + return agentNameClaude +} + +// ModelEnvVar exposes the environment variable used to override the model. +func (claudeAgent) ModelEnvVar() string { + // Claude Code reads its target model from CLAUDE_REVIEW_MODEL. + return "CLAUDE_REVIEW_MODEL" +} + +// BuildCommand constructs the Claude CLI invocation for a review run. +func (claudeAgent) BuildCommand(ctx context.Context, inv agentInvocation) (*exec.Cmd, error) { + // When running Claude Code in non-interactive mode, the only output format + // that gives regular progress updates is stream-json - anything else waits + // for the full analysis to complete and then provides all the output at + // once. It would be nice if Claude Code had something like a stream-text + // mode, and there's a request for that here: + // https://github.com/anthropics/claude-code/issues/4346 + // In the meantime, I think we'll just live with the JSON output, since at + // least that gives some indication of progress and what's happening. + args := []string{ + "--print", "--verbose", + "--output-format", "stream-json", + "--dangerously-skip-permissions", + } + if strings.TrimSpace(inv.Model) != "" { + args = append(args, "--model", inv.Model) + } + if strings.TrimSpace(inv.ExtraArgs) != "" { + parsed, err := shellwords.Parse(inv.ExtraArgs) + if err != nil { + return nil, fmt.Errorf("parse extra args: %w", err) + } + args = append(args, parsed...) + } + + cmd := exec.CommandContext(ctx, "claude", args...) + cmd.Stdin = strings.NewReader(inv.Prompt) + if inv.WorkingDir != "" { + cmd.Dir = inv.WorkingDir + } + + return cmd, nil +} diff --git a/agents/security-reviewer/reviewer/codex.go b/agents/security-reviewer/reviewer/codex.go new file mode 100644 index 00000000..7aac74b7 --- /dev/null +++ b/agents/security-reviewer/reviewer/codex.go @@ -0,0 +1,52 @@ +package main + +import ( + "context" + "fmt" + "os/exec" + "strings" + + "github.com/mattn/go-shellwords" +) + +// codexAgent implements reviewerAgent for the OpenAI Codex CLI. +type codexAgent struct{} + +// Name returns the stable identifier for the Codex agent implementation. +func (codexAgent) Name() string { + return agentNameCodex +} + +// ModelEnvVar exposes the environment variable used to override Codex models. +func (codexAgent) ModelEnvVar() string { + // Codex shells read from CODEX_REVIEW_MODEL when provided. + return "CODEX_REVIEW_MODEL" +} + +// BuildCommand constructs the Codex CLI invocation for a review run. +func (codexAgent) BuildCommand(ctx context.Context, inv agentInvocation) (*exec.Cmd, error) { + args := []string{ + "exec", + "--skip-git-repo-check", + "--dangerously-bypass-approvals-and-sandbox", + } + if strings.TrimSpace(inv.Model) != "" { + args = append(args, "--model", inv.Model) + } + if strings.TrimSpace(inv.ExtraArgs) != "" { + parsed, err := shellwords.Parse(inv.ExtraArgs) + if err != nil { + return nil, fmt.Errorf("parse extra args: %w", err) + } + args = append(args, parsed...) + } + args = append(args, "-") + + cmd := exec.CommandContext(ctx, "codex", args...) + cmd.Stdin = strings.NewReader(inv.Prompt) + if inv.WorkingDir != "" { + cmd.Dir = inv.WorkingDir + } + + return cmd, nil +} diff --git a/agents/security-reviewer/reviewer/main.go b/agents/security-reviewer/reviewer/main.go new file mode 100644 index 00000000..72ea74e3 --- /dev/null +++ b/agents/security-reviewer/reviewer/main.go @@ -0,0 +1,392 @@ +package main + +import ( + "context" + "errors" + "fmt" + "io/fs" + "os" + "os/signal" + "path/filepath" + "strconv" + "strings" + "syscall" + "time" +) + +const ( + // promptTemplatePath is the location of the static prompt template bundled with the image. + promptTemplatePath = "/opt/security-reviewer/prompt-template.md" + // reportTemplatePath is the location of the report template referenced in prompts. + reportTemplatePath = "/opt/security-reviewer/report-template.md" + // defaultPromptPath is where the rendered prompt is written for the agent to consume. + defaultPromptPath = "/workspace/input/prompt.md" + // defaultRepositoryPath is the mount point containing the repository under review. + defaultRepositoryPath = "/workspace/input/repository" + // defaultReportPath is the expected location for the agent's security report. + defaultReportPath = "/workspace/output/report.md" + // defaultLabelsPath is the expected location for the agent's label output. + defaultLabelsPath = "/workspace/output/labels.txt" + // defaultReviewAgent is the reviewer implementation used when none is specified. + defaultReviewAgent = "claude" + // defaultAgentWorkingDir is the directory from which the agent executes. + defaultAgentWorkingDir = "/workspace" + // defaultTimeout bounds how long the reviewer will wait for the agent to complete. + defaultTimeout = time.Hour + + // envReviewAgent selects which reviewer agent to run. + envReviewAgent = "REVIEW_AGENT" + // envAgentExtraArgs contains optional CLI arguments passed through to the agent. + envAgentExtraArgs = "REVIEW_AGENT_EXTRA_ARGS" + // envReviewHeadSHA provides the head commit SHA under review. + envReviewHeadSHA = "REVIEW_HEAD_SHA" + // envReviewBaseSHA provides the base commit SHA when performing differential reviews. + envReviewBaseSHA = "REVIEW_BASE_SHA" + // envReviewTarget supplies a human-readable label describing the review target. + envReviewTarget = "REVIEW_TARGET_LABEL" + // envReviewTimeout allows callers to override the agent execution timeout in seconds. + envReviewTimeout = "REVIEW_TIMEOUT_SECS" +) + +// ReviewMode enumerates supported security review modes. +type ReviewMode string + +const ( + // ReviewModeFull requests a full repository audit. + ReviewModeFull ReviewMode = "full" + // ReviewModeDifferential requests a differential review between two commits. + ReviewModeDifferential ReviewMode = "differential" +) + +// agentInvocation captures execution hints per reviewer agent. +type agentInvocation struct { + // Prompt is the rendered instruction text passed over stdin. + Prompt string + // Model identifies the model to invoke, when the agent supports overrides. + Model string + // ExtraArgs contains caller-supplied CLI arguments for the agent. + ExtraArgs string + // WorkingDir specifies the directory where the agent command executes. + WorkingDir string +} + +// promptPlaceholders stores values substituted into the static prompt template. +type promptPlaceholders struct { + // ModeLabel is the human friendly descriptor for the review mode. + ModeLabel string + // ModeSummary highlights the responsibilities for the current mode. + ModeSummary string + // TargetLabel is an identifier referencing the repository under review. + TargetLabel string + // RepositoryPath points to the checked-out repository mount. + RepositoryPath string + // HeadCommit is the commit under audit. + HeadCommit string + // BaseCommit is the comparison commit for diff reviews. + BaseCommit string + // CommitRange renders the ... spec for diff reviews. + CommitRange string + // GitDiffHint guides the agent on how to inspect the change set. + GitDiffHint string + // ReportPath denotes where the agent should write its report. + ReportPath string + // LabelsPath denotes where the agent should write labels for automation. + LabelsPath string + // ReportTemplatePath tells the agent which template to follow exactly. + ReportTemplatePath string +} + +// main configures logging, resolves environment, and runs the selected agent. +func main() { + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + + // Run the review workflow and exit non-zero on failure so the container signals an error. + if err := run(ctx); err != nil { + logError(err) + os.Exit(1) + } +} + +// run orchestrates prompt generation and agent execution. +func run(ctx context.Context) error { + // Parse review configuration from the environment. + headSHA, err := fetchEnv(envReviewHeadSHA, true) + if err != nil { + return err + } + baseSHA, err := fetchEnv(envReviewBaseSHA, false) + if err != nil { + return err + } + + mode := ReviewModeFull + if baseSHA != "" { + mode = ReviewModeDifferential + } + + targetLabel, err := fetchEnv(envReviewTarget, false) + if err != nil { + return err + } + + // Ensure the repository mount is present before processing. + if err = ensureDirectory(defaultRepositoryPath); err != nil { + return err + } + + promptContent, err := buildPromptContent(mode, targetLabel, headSHA, baseSHA) + if err != nil { + return err + } + if err = ensureParent(defaultPromptPath); err != nil { + return err + } + if err = os.WriteFile(defaultPromptPath, []byte(promptContent), 0o644); err != nil { + return fmt.Errorf("write prompt: %w", err) + } + logInfo(fmt.Sprintf("Rendered prompt to %s.", defaultPromptPath)) + + // Select the reviewer implementation and build invocation parameters. + agentName, err := fetchEnv(envReviewAgent, false) + if err != nil { + return err + } + if agentName == "" { + agentName = defaultReviewAgent + } + agentKey := strings.ToLower(strings.TrimSpace(agentName)) + agent, err := selectAgent(agentKey) + if err != nil { + return err + } + + var model string + if envName := agent.ModelEnvVar(); envName != "" { + model, err = fetchEnv(envName, false) + if err != nil { + return err + } + } + + extraArgs, _ := fetchEnv(envAgentExtraArgs, false) + inv := agentInvocation{ + Prompt: promptContent, + Model: model, + ExtraArgs: extraArgs, + WorkingDir: defaultAgentWorkingDir, + } + + timeout, err := resolveTimeout() + if err != nil { + return err + } + + agentCtx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + if mode == ReviewModeDifferential { + logInfo(fmt.Sprintf( + "Starting differential review (agent=%s head=%s base=%s label=%s).", + agent.Name(), headSHA, baseSHA, targetLabel, + )) + } else { + logInfo(fmt.Sprintf( + "Starting full review (agent=%s head=%s label=%s).", + agent.Name(), headSHA, targetLabel, + )) + } + + // Execute the agent command and relay its output streams. + if err := runAgent(agentCtx, agent, inv); err != nil { + return err + } + + // Inspect the outputs and warn if they were not produced. + if fileExists(defaultReportPath) { + logInfo(fmt.Sprintf("Report stored at %s.", defaultReportPath)) + } else { + logWarn(fmt.Sprintf("Report not produced at %s.", defaultReportPath)) + } + if fileExists(defaultLabelsPath) { + logInfo(fmt.Sprintf("Labels stored at %s.", defaultLabelsPath)) + } else { + logWarn(fmt.Sprintf("Labels not produced at %s.", defaultLabelsPath)) + } + + logInfo("Security review completed successfully.") + return nil +} + +// fetchEnv reads an environment variable and validates presence when required. +func fetchEnv(name string, required bool) (string, error) { + value := strings.TrimSpace(os.Getenv(name)) + if value == "" && required { + return "", fmt.Errorf("missing required environment variable: %s", name) + } + return value, nil +} + +// ensureParent creates directories needed for the provided path. +func ensureParent(path string) error { + dir := filepath.Dir(path) + if dir == "." || dir == "" { + return nil + } + return os.MkdirAll(dir, 0o755) +} + +// runAgent executes the reviewer agent command and captures output streams. +func runAgent(ctx context.Context, agent reviewerAgent, inv agentInvocation) error { + cmd, err := agent.BuildCommand(ctx, inv) + if err != nil { + return err + } + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err = cmd.Run(); err != nil { + return fmt.Errorf("%s invocation failed: %w", agent.Name(), err) + } + return nil +} + +func resolveTimeout() (time.Duration, error) { + value := strings.TrimSpace(os.Getenv(envReviewTimeout)) + if value == "" { + return defaultTimeout, nil + } + secs, err := strconv.Atoi(value) + if err != nil || secs <= 0 { + return 0, fmt.Errorf("invalid %s value %q", envReviewTimeout, value) + } + return time.Duration(secs) * time.Second, nil +} + +// buildPromptContent renders a concrete prompt for the selected review mode. +func buildPromptContent(mode ReviewMode, targetLabel, headSHA, baseSHA string) (string, error) { + displayLabel := strings.TrimSpace(targetLabel) + if displayLabel == "" { + displayLabel = "[Not provided]" + } + baseDisplay := "[Not applicable]" + commitRange := "[Not applicable]" + if mode == ReviewModeDifferential { + baseDisplay = baseSHA + commitRange = fmt.Sprintf("%s...%s", baseSHA, headSHA) + } + + ph := promptPlaceholders{ + ModeLabel: modeLabel(mode), + ModeSummary: modeSummary(mode), + TargetLabel: displayLabel, + RepositoryPath: defaultRepositoryPath, + HeadCommit: headSHA, + BaseCommit: baseDisplay, + CommitRange: commitRange, + GitDiffHint: gitDiffHint(mode, baseSHA, headSHA), + ReportPath: defaultReportPath, + LabelsPath: defaultLabelsPath, + ReportTemplatePath: reportTemplatePath, + } + return renderPrompt(ph) +} + +// renderPrompt injects placeholder values into the prompt template. +func renderPrompt(ph promptPlaceholders) (string, error) { + templateBytes, err := os.ReadFile(promptTemplatePath) + if err != nil { + return "", fmt.Errorf("read prompt template: %w", err) + } + replacer := strings.NewReplacer( + "$MODE_LABEL", ph.ModeLabel, + "$MODE_SUMMARY", ph.ModeSummary, + "$TARGET_LABEL", ph.TargetLabel, + "$REPOSITORY_PATH", ph.RepositoryPath, + "$HEAD_COMMIT", ph.HeadCommit, + "$BASE_COMMIT", ph.BaseCommit, + "$COMMIT_RANGE", ph.CommitRange, + "$GIT_DIFF_HINT", ph.GitDiffHint, + "$REPORT_PATH", ph.ReportPath, + "$LABELS_PATH", ph.LabelsPath, + "$REPORT_TEMPLATE_PATH", ph.ReportTemplatePath, + ) + return replacer.Replace(string(templateBytes)), nil +} + +// gitDiffHint conveys how the agent should inspect the repository state. +func gitDiffHint(mode ReviewMode, baseSHA, headSHA string) string { + if mode == ReviewModeDifferential { + return fmt.Sprintf("Run `git diff %s...%s` (and related commands) inside %s to inspect the change set.", baseSHA, headSHA, defaultRepositoryPath) + } + return "Audit the entire working tree at the head commit." +} + +// modeLabel converts a review mode to a user friendly label. +func modeLabel(mode ReviewMode) string { + switch mode { + case ReviewModeDifferential: + return "Differential" + case ReviewModeFull: + return "Full" + default: + return "Unknown" + } +} + +// modeSummary explains the responsibilities associated with a review mode. +func modeSummary(mode ReviewMode) string { + switch mode { + case ReviewModeDifferential: + return "You are reviewing the changes introduced in a Git repository between the specified base and head commits. Prioritize spotting deliberately malicious additions alongside accidental vulnerabilities." + case ReviewModeFull: + return "You are auditing a Git repository snapshot at the specified head commit. Assume attackers may have hidden malicious logic and hunt for both intentional and accidental risks." + default: + return "The review mode is unknown." + } +} + +// fileExists returns true when a non-zero length file exists at path. +func fileExists(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + if info.Mode()&os.ModeType != 0 { + return false + } + return info.Size() > 0 +} + +// logInfo prints informational messages prefixed for clarity. +func logInfo(msg string) { + fmt.Printf("[security-reviewer] %s\n", msg) +} + +// logWarn prints warning messages prefixed for clarity. +func logWarn(msg string) { + fmt.Printf("[security-reviewer] WARNING: %s\n", msg) +} + +// logError prints error messages prefixed for clarity. +func logError(err error) { + var pathErr *fs.PathError + if errors.As(err, &pathErr) { + fmt.Fprintf(os.Stderr, "[security-reviewer] ERROR: %s (%s)\n", pathErr.Path, pathErr.Err) + return + } + fmt.Fprintf(os.Stderr, "[security-reviewer] ERROR: %s\n", err) +} + +// ensureDirectory verifies that the supplied path exists and is a directory. +func ensureDirectory(path string) error { + info, err := os.Stat(path) + if err != nil { + return fmt.Errorf("stat directory %s: %w", path, err) + } + if !info.IsDir() { + return fmt.Errorf("%s is not a directory", path) + } + return nil +} diff --git a/cmd/build/main.go b/cmd/build/main.go index 656f2346..1c378de6 100644 --- a/cmd/build/main.go +++ b/cmd/build/main.go @@ -12,7 +12,6 @@ import ( "strings" "github.com/docker/mcp-registry/internal/mcp" - "github.com/docker/mcp-registry/pkg/github" "github.com/docker/mcp-registry/pkg/servers" ) @@ -117,39 +116,19 @@ func buildDockerEnv(additionalEnv ...string) []string { } func buildMcpImage(ctx context.Context, server servers.Server) error { - projectURL := server.Source.Project - branch := server.Source.Branch - directory := server.Source.Directory - - client := github.New() - - repository, err := client.GetProjectRepository(ctx, projectURL) - if err != nil { - return err - } - - if branch == "" { - branch = repository.GetDefaultBranch() + commit := server.Source.Commit + if commit == "" { + return fmt.Errorf("local server %s must specify source.commit before building", server.Name) } - sha, err := client.GetCommitSHA1(ctx, projectURL, branch) - if err != nil { - return err - } - - gitURL := projectURL + ".git#" - if branch != "" { - gitURL += branch - } - if directory != "" && directory != "." { - gitURL += ":" + directory - } + gitURL := server.GetContext() var cmd *exec.Cmd token := os.Getenv("GITHUB_TOKEN") buildArgs := []string{ - "-f", server.GetDockerfile(), "-t", "check", "-t", server.Image, "--label", "org.opencontainers.image.revision=" + sha, "--load", + "-f", server.GetDockerfile(), "-t", "check", "-t", server.Image, + "--label", "org.opencontainers.image.revision=" + commit, "--load", } if server.Source.BuildTarget != "" { diff --git a/cmd/ci/collect_full_audit.go b/cmd/ci/collect_full_audit.go new file mode 100644 index 00000000..c8dbb804 --- /dev/null +++ b/cmd/ci/collect_full_audit.go @@ -0,0 +1,111 @@ +/* +Copyright © 2025 Docker, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package main + +import ( + "errors" + "flag" + "io/fs" + "path/filepath" + "strings" +) + +// runCollectFullAudit enumerates local servers (optionally filtered) and writes +// their metadata to a JSON file for manual auditing. It expects --workspace, +// --servers, and --output-json flags. +func runCollectFullAudit(args []string) error { + flags := flag.NewFlagSet("collect-full-audit", flag.ContinueOnError) + workspace := flags.String("workspace", ".", "path to repository workspace") + filter := flags.String("servers", "", "optional comma-separated server filter") + outputJSON := flags.String("output-json", "", "path to write JSON context") + if err := flags.Parse(args); err != nil { + return err + } + + if *outputJSON == "" { + return errors.New("output-json is required") + } + + targets, err := collectAuditTargets(*workspace, *filter) + if err != nil { + return err + } + + if len(targets) == 0 { + removeIfPresent(*outputJSON) + return nil + } + + return writeJSONFile(*outputJSON, targets) +} + +// collectAuditTargets returns audit targets for all local servers or a filtered +// subset based on the supplied comma-separated list. +func collectAuditTargets(workspace, filter string) ([]auditTarget, error) { + filterSet := make(map[string]struct{}) + for _, name := range splitList(filter) { + filterSet[name] = struct{}{} + } + + var targets []auditTarget + err := filepath.WalkDir(filepath.Join(workspace, "servers"), func(path string, entry fs.DirEntry, walkErr error) error { + if walkErr != nil { + return walkErr + } + if entry.IsDir() || !strings.HasSuffix(path, "server.yaml") { + return nil + } + + relative := strings.TrimPrefix(path, workspace+string(filepath.Separator)) + doc, err := loadServerYAMLFromWorkspace(workspace, relative) + if err != nil || !isLocalServer(doc) { + return nil + } + + server := filepath.Base(filepath.Dir(path)) + if len(filterSet) > 0 { + if _, ok := filterSet[strings.ToLower(server)]; !ok { + return nil + } + } + + project := strings.TrimSpace(doc.Source.Project) + commit := strings.TrimSpace(doc.Source.Commit) + if project == "" || commit == "" { + return nil + } + + targets = append(targets, auditTarget{ + Server: server, + Project: project, + Commit: commit, + Directory: strings.TrimSpace(doc.Source.Directory), + }) + return nil + }) + if err != nil { + return nil, err + } + + return targets, nil +} diff --git a/cmd/ci/collect_new_servers.go b/cmd/ci/collect_new_servers.go new file mode 100644 index 00000000..517aebef --- /dev/null +++ b/cmd/ci/collect_new_servers.go @@ -0,0 +1,137 @@ +/* +Copyright © 2025 Docker, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package main + +import ( + "errors" + "flag" + "fmt" + "os" + "path/filepath" + "strings" +) + +// runCollectNewServers identifies newly added local servers between two git +// revisions. It accepts --base, --head, --workspace, --output-json, and +// --summary-md flags, writing machine-readable targets and a Markdown summary +// for reviewers. +func runCollectNewServers(args []string) error { + flags := flag.NewFlagSet("collect-new-servers", flag.ContinueOnError) + base := flags.String("base", "", "base git commit SHA") + head := flags.String("head", "", "head git commit SHA") + workspace := flags.String("workspace", ".", "path to repository workspace") + outputJSON := flags.String("output-json", "", "path to write JSON context") + summaryMD := flags.String("summary-md", "", "path to write Markdown summary") + if err := flags.Parse(args); err != nil { + return err + } + + if *base == "" || *head == "" || *outputJSON == "" || *summaryMD == "" { + return errors.New("base, head, output-json, and summary-md are required") + } + + targets, err := collectNewServerTargets(*workspace, *base, *head) + if err != nil { + return err + } + + if len(targets) == 0 { + removeIfPresent(*outputJSON) + removeIfPresent(*summaryMD) + return nil + } + + if err := writeJSONFile(*outputJSON, targets); err != nil { + return err + } + + summary := buildNewServerSummary(targets) + return os.WriteFile(*summaryMD, []byte(summary), 0o644) +} + +// collectNewServerTargets returns metadata for local servers that were added +// between the supplied git revisions. +func collectNewServerTargets(workspace, base, head string) ([]newServerTarget, error) { + lines, err := gitDiff(workspace, base, head, "--name-status") + if err != nil { + return nil, err + } + + var targets []newServerTarget + for _, line := range lines { + if !strings.HasPrefix(line, "A\t") { + continue + } + path := strings.TrimPrefix(line, "A\t") + if !strings.HasPrefix(path, "servers/") || !strings.HasSuffix(path, "server.yaml") { + continue + } + + doc, err := loadServerYAMLFromWorkspace(workspace, path) + if err != nil { + continue + } + + if !isLocalServer(doc) { + continue + } + + project := strings.TrimSpace(doc.Source.Project) + commit := strings.TrimSpace(doc.Source.Commit) + if project == "" || commit == "" { + continue + } + + targets = append(targets, newServerTarget{ + Server: filepath.Base(filepath.Dir(path)), + File: path, + Image: strings.TrimSpace(doc.Image), + Project: project, + Commit: commit, + Directory: strings.TrimSpace(doc.Source.Directory), + }) + } + + return targets, nil +} + +// buildNewServerSummary renders Markdown describing newly added servers for +// review prompts and human consumption. +func buildNewServerSummary(targets []newServerTarget) string { + builder := strings.Builder{} + builder.WriteString("## New Local Servers\n\n") + + for _, target := range targets { + builder.WriteString(fmt.Sprintf("### %s\n", target.Server)) + builder.WriteString(fmt.Sprintf("- Repository: %s\n", target.Project)) + builder.WriteString(fmt.Sprintf("- Commit: `%s`\n", target.Commit)) + if target.Directory != "" { + builder.WriteString(fmt.Sprintf("- Directory: %s\n", target.Directory)) + } else { + builder.WriteString("- Directory: (repository root)\n") + } + builder.WriteString(fmt.Sprintf("- Checkout path: /tmp/security-review/new/%s/repo\n\n", target.Server)) + } + + return builder.String() +} diff --git a/cmd/ci/collect_updated_pins.go b/cmd/ci/collect_updated_pins.go new file mode 100644 index 00000000..0b85c27c --- /dev/null +++ b/cmd/ci/collect_updated_pins.go @@ -0,0 +1,141 @@ +/* +Copyright © 2025 Docker, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package main + +import ( + "errors" + "flag" + "fmt" + "os" + "path/filepath" + "strings" +) + +// runCollectUpdatedPins gathers metadata for servers that updated their commit +// pins between two git revisions. It expects --base, --head, --workspace, +// --output-json, and --summary-md arguments. The identified targets are written +// to the JSON file while a Markdown summary is produced for humans. +func runCollectUpdatedPins(args []string) error { + flags := flag.NewFlagSet("collect-updated-pins", flag.ContinueOnError) + base := flags.String("base", "", "base git commit SHA") + head := flags.String("head", "", "head git commit SHA") + workspace := flags.String("workspace", ".", "path to repository workspace") + outputJSON := flags.String("output-json", "", "path to write JSON context") + summaryMD := flags.String("summary-md", "", "path to write Markdown summary") + if err := flags.Parse(args); err != nil { + return err + } + + if *base == "" || *head == "" || *outputJSON == "" || *summaryMD == "" { + return errors.New("base, head, output-json, and summary-md are required") + } + + targets, err := collectUpdatedPinTargets(*workspace, *base, *head) + if err != nil { + return err + } + + if len(targets) == 0 { + removeIfPresent(*outputJSON) + removeIfPresent(*summaryMD) + return nil + } + + if err := writeJSONFile(*outputJSON, targets); err != nil { + return err + } + + summary := buildPinSummary(targets) + return os.WriteFile(*summaryMD, []byte(summary), 0o644) +} + +// collectUpdatedPinTargets identifies local servers whose pinned commits differ +// between the supplied git revisions and returns their metadata for further +// processing. +func collectUpdatedPinTargets(workspace, base, head string) ([]pinTarget, error) { + paths, err := gitDiff(workspace, base, head, "--name-only") + if err != nil { + return nil, err + } + + var targets []pinTarget + for _, relative := range paths { + if !strings.HasPrefix(relative, "servers/") || !strings.HasSuffix(relative, "server.yaml") { + continue + } + + baseDoc, err := loadServerYAMLAt(workspace, base, relative) + if err != nil { + continue + } + headDoc, err := loadServerYAMLFromWorkspace(workspace, relative) + if err != nil { + continue + } + + if !isLocalServer(headDoc) || !isLocalServer(baseDoc) { + continue + } + + oldCommit := strings.TrimSpace(baseDoc.Source.Commit) + newCommit := strings.TrimSpace(headDoc.Source.Commit) + project := strings.TrimSpace(headDoc.Source.Project) + if oldCommit == "" || newCommit == "" || oldCommit == newCommit || project == "" { + continue + } + + targets = append(targets, pinTarget{ + Server: filepath.Base(filepath.Dir(relative)), + File: relative, + Image: strings.TrimSpace(headDoc.Image), + Project: project, + Directory: strings.TrimSpace(headDoc.Source.Directory), + OldCommit: oldCommit, + NewCommit: newCommit, + }) + } + + return targets, nil +} + +// buildPinSummary renders a Markdown section describing updated pin targets so +// that review tooling and humans can understand what changed. +func buildPinSummary(targets []pinTarget) string { + builder := strings.Builder{} + builder.WriteString("## Updated Commit Pins\n\n") + + for _, target := range targets { + builder.WriteString(fmt.Sprintf("### %s\n", target.Server)) + builder.WriteString(fmt.Sprintf("- Repository: %s\n", target.Project)) + if target.Directory != "" { + builder.WriteString(fmt.Sprintf("- Directory: %s\n", target.Directory)) + } else { + builder.WriteString("- Directory: (repository root)\n") + } + builder.WriteString(fmt.Sprintf("- Previous commit: `%s`\n", target.OldCommit)) + builder.WriteString(fmt.Sprintf("- New commit: `%s`\n", target.NewCommit)) + builder.WriteString(fmt.Sprintf("- Diff path: /tmp/security-review/pins/%s/diff.patch\n\n", target.Server)) + } + + return builder.String() +} diff --git a/cmd/ci/helpers.go b/cmd/ci/helpers.go new file mode 100644 index 00000000..7b8f72a7 --- /dev/null +++ b/cmd/ci/helpers.go @@ -0,0 +1,146 @@ +/* +Copyright © 2025 Docker, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package main + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "gopkg.in/yaml.v3" + + "github.com/docker/mcp-registry/pkg/servers" +) + +// writeJSONFile stores the provided value as indented JSON at the given path. +func writeJSONFile(path string, value any) error { + payload, err := json.MarshalIndent(value, "", " ") + if err != nil { + return err + } + return os.WriteFile(path, payload, 0o644) +} + +// readJSONFile populates value with JSON data read from the provided path. +func readJSONFile(path string, value any) error { + content, err := os.ReadFile(path) + if err != nil { + return err + } + return json.Unmarshal(content, value) +} + +// removeIfPresent deletes the file at the path when it exists. +func removeIfPresent(path string) { + if path == "" { + return + } + if _, err := os.Stat(path); err == nil { + _ = os.Remove(path) + } +} + +// loadServerYAMLFromWorkspace loads a server YAML file located in the workspace. +func loadServerYAMLFromWorkspace(workspace, relative string) (servers.Server, error) { + fullPath := filepath.Join(workspace, relative) + content, err := os.ReadFile(fullPath) + if err != nil { + return servers.Server{}, err + } + return decodeServerDocument(content) +} + +// loadServerYAMLAt loads a server YAML file from the git history at the commit. +func loadServerYAMLAt(workspace, commit, relative string) (servers.Server, error) { + out, err := runGitCommand(workspace, "show", fmt.Sprintf("%s:%s", commit, relative)) + if err != nil { + return servers.Server{}, err + } + return decodeServerDocument([]byte(out)) +} + +// decodeServerDocument converts raw YAML bytes into a servers.Server. +func decodeServerDocument(raw []byte) (servers.Server, error) { + var doc servers.Server + if err := yaml.Unmarshal(raw, &doc); err != nil { + return servers.Server{}, err + } + return doc, nil +} + +// isLocalServer returns true when the definition corresponds to a local server image. +func isLocalServer(doc servers.Server) bool { + if !strings.EqualFold(doc.Type, "server") { + return false + } + return strings.HasPrefix(strings.TrimSpace(doc.Image), "mcp/") +} + +// gitDiff runs git diff for server YAML files and returns the resulting paths. +func gitDiff(workspace, base, head, mode string) ([]string, error) { + args := []string{"diff", mode, base, head, "--", "servers/*/server.yaml"} + out, err := runGitCommand(workspace, args...) + if err != nil { + return nil, err + } + + var lines []string + for _, line := range strings.Split(strings.TrimSpace(out), "\n") { + line = strings.TrimSpace(line) + if line != "" { + lines = append(lines, line) + } + } + return lines, nil +} + +// runGitCommand executes git with the given arguments inside the directory. +func runGitCommand(dir string, args ...string) (string, error) { + cmd := exec.Command("git", args...) + cmd.Dir = dir + output, err := cmd.CombinedOutput() + if err != nil { + return "", fmt.Errorf("git %s: %w\n%s", strings.Join(args, " "), err, string(output)) + } + return string(output), nil +} + +// splitList normalizes a delimited string into lowercase server names. +func splitList(raw string) []string { + if raw == "" { + return nil + } + var values []string + for _, segment := range strings.FieldsFunc(raw, func(r rune) bool { + return r == ',' || r == '\n' || r == ' ' || r == '\t' + }) { + value := strings.TrimSpace(segment) + if value != "" { + values = append(values, strings.ToLower(value)) + } + } + return values +} diff --git a/cmd/ci/main.go b/cmd/ci/main.go new file mode 100644 index 00000000..c2c66fb9 --- /dev/null +++ b/cmd/ci/main.go @@ -0,0 +1,59 @@ +/* +Copyright © 2025 Docker, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package main + +import ( + "fmt" + "os" +) + +// main dispatches the CLI to a specific sub-command implementation. +func main() { + if len(os.Args) < 2 { + fmt.Fprintln(os.Stderr, "usage: ci [options]") + os.Exit(2) + } + + cmd := os.Args[1] + args := os.Args[2:] + + var err error + switch cmd { + case "collect-updated-pins": + err = runCollectUpdatedPins(args) + case "collect-new-servers": + err = runCollectNewServers(args) + case "collect-full-audit": + err = runCollectFullAudit(args) + case "update-pins": + err = runUpdatePins(args) + default: + fmt.Fprintf(os.Stderr, "unknown command: %s\n", cmd) + os.Exit(2) + } + + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/cmd/ci/types.go b/cmd/ci/types.go new file mode 100644 index 00000000..98d95572 --- /dev/null +++ b/cmd/ci/types.go @@ -0,0 +1,69 @@ +/* +Copyright © 2025 Docker, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package main + +// pinTarget describes a server that updated its commit pin within a pull request. +type pinTarget struct { + // Server is the registry entry name (directory) that was updated. + Server string `json:"server"` + // File is the relative YAML path that changed for the server. + File string `json:"file"` + // Image is the Docker image identifier associated with the server. + Image string `json:"image"` + // Project is the upstream repository URL for the server source. + Project string `json:"project"` + // Directory points to the subdirectory inside the upstream repository, when set. + Directory string `json:"directory,omitempty"` + // OldCommit contains the previously pinned commit SHA. + OldCommit string `json:"old_commit"` + // NewCommit contains the newly pinned commit SHA. + NewCommit string `json:"new_commit"` +} + +// newServerTarget captures metadata for a newly added local server. +type newServerTarget struct { + // Server is the registry entry name for the newly added server. + Server string `json:"server"` + // File is the YAML file that defines the server in the registry. + File string `json:"file"` + // Image is the Docker image identifier associated with the new server. + Image string `json:"image"` + // Project is the upstream repository URL that hosts the server code. + Project string `json:"project"` + // Commit is the pinned commit SHA for the newly added server. + Commit string `json:"commit"` + // Directory specifies a subdirectory inside the upstream repository, when present. + Directory string `json:"directory,omitempty"` +} + +// auditTarget represents a server selected for a manual full audit. +type auditTarget struct { + // Server is the registry entry name included in the manual audit. + Server string `json:"server"` + // Project is the upstream repository URL for the audited server. + Project string `json:"project"` + // Commit is the pinned commit SHA to audit. + Commit string `json:"commit"` + // Directory is the subdirectory within the upstream repo to inspect, when applicable. + Directory string `json:"directory,omitempty"` +} diff --git a/cmd/ci/update_pins.go b/cmd/ci/update_pins.go new file mode 100644 index 00000000..836c0a5d --- /dev/null +++ b/cmd/ci/update_pins.go @@ -0,0 +1,209 @@ +/* +Copyright © 2025 Docker, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package main + +import ( + "context" + "flag" + "fmt" + "os" + "path/filepath" + "regexp" + "sort" + "strings" + + "github.com/docker/mcp-registry/pkg/github" + "github.com/docker/mcp-registry/pkg/servers" +) + +// runUpdatePins refreshes pinned commits for local servers by resolving the +// latest upstream revision on the tracked branch and updating server YAML +// definitions in place. Accepts a --servers flag to limit which servers to +// update. Emits a summary of modified servers on stdout; errors are reported +// per server so that a single failure does not abort the entire sweep. +func runUpdatePins(args []string) error { + fs := flag.NewFlagSet("update-pins", flag.ExitOnError) + serversFlag := fs.String("servers", "", "Comma-separated list of servers to update (leave blank for all)") + if err := fs.Parse(args); err != nil { + return err + } + + ctx := context.Background() + + // Parse the servers filter if provided. + allowedServers := make(map[string]bool) + if *serversFlag != "" { + for _, name := range splitList(*serversFlag) { + allowedServers[name] = true + } + } + + entries, err := os.ReadDir("servers") + if err != nil { + return fmt.Errorf("reading servers directory: %w", err) + } + + var updated []string + for _, entry := range entries { + if !entry.IsDir() { + continue + } + + // Skip servers not in the filter list (if provided). + serverName := strings.ToLower(entry.Name()) + if len(allowedServers) > 0 && !allowedServers[serverName] { + continue + } + + serverPath := filepath.Join("servers", entry.Name(), "server.yaml") + + // Parse the server definition so that we can evaluate eligibility and + // discover the backing GitHub repository and branch information. + server, err := servers.Read(serverPath) + if err != nil { + fmt.Fprintf(os.Stderr, "reading %s: %v\n", serverPath, err) + continue + } + + if server.Type != "server" { + continue + } + if !strings.HasPrefix(server.Image, "mcp/") { + continue + } + if server.Source.Project == "" { + continue + } + + // Only repositories hosted on GitHub can be advanced by this command, + // because the helper client relies on the GitHub API for commit lookup. + if !strings.Contains(server.Source.Project, "github.com/") { + fmt.Printf("Skipping %s: project is not hosted on GitHub.\n", server.Name) + continue + } + + existing := strings.ToLower(server.Source.Commit) + if existing == "" { + fmt.Printf("Skipping %s: no pinned commit present.\n", server.Name) + continue + } + + client := github.NewFromServer(server) + // Resolve the latest commit on the configured branch so we can refresh + // the pin if it has advanced since the last sweep. + latest, err := client.GetCommitSHA1(ctx, server.Source.Project, server.GetBranch()) + if err != nil { + fmt.Fprintf(os.Stderr, "fetching commit for %s: %v\n", server.Name, err) + continue + } + latest = strings.ToLower(latest) + + changed, err := writePinnedCommit(serverPath, latest) + if err != nil { + fmt.Fprintf(os.Stderr, "updating %s: %v\n", server.Name, err) + continue + } + + if existing != latest { + fmt.Printf("Updated %s: %s -> %s\n", server.Name, existing, latest) + } else if changed { + fmt.Printf("Reformatted pinned commit for %s at %s\n", server.Name, latest) + } + + if changed { + updated = append(updated, server.Name) + } + } + + if len(updated) == 0 { + fmt.Println("No commit updates required.") + return nil + } + + sort.Strings(updated) + fmt.Println("Servers with updated pins:", strings.Join(updated, ", ")) + return nil +} + +// writePinnedCommit replaces the commit field inside the source block with the +// provided SHA while preserving formatting. A boolean indicates whether the +// file changed. +func writePinnedCommit(path string, updated string) (bool, error) { + content, err := os.ReadFile(path) + if err != nil { + return false, err + } + + lines := strings.Split(string(content), "\n") + sourceIndex := -1 + for i, line := range lines { + if strings.HasPrefix(line, "source:") { + sourceIndex = i + break + } + } + if sourceIndex == -1 { + return false, fmt.Errorf("no source block found in %s", path) + } + + // Scan the nested source block until we locate the commit attribute, + // capturing its indentation so that formatting survives the rewrite. + commitIndex := -1 + indent := "" + commitPattern := regexp.MustCompile(`^([ \t]+)commit:\s*[a-fA-F0-9]{40}\s*$`) + for i := sourceIndex + 1; i < len(lines); i++ { + line := lines[i] + if !strings.HasPrefix(line, " ") { + break + } + + if match := commitPattern.FindStringSubmatch(line); match != nil { + commitIndex = i + indent = match[1] + break + } + } + + if commitIndex < 0 { + return false, fmt.Errorf("no commit line found in %s", path) + } + + // Replace only the commit value so that other keys maintain their + // original ordering and indentation. + newLine := indent + "commit: " + updated + lines[commitIndex] = newLine + + output := strings.Join(lines, "\n") + if !strings.HasSuffix(output, "\n") { + output += "\n" + } + + if output == string(content) { + return false, nil + } + + if err := os.WriteFile(path, []byte(output), 0o644); err != nil { + return false, err + } + return true, nil +} diff --git a/cmd/clean/main.go b/cmd/clean/main.go new file mode 100644 index 00000000..27ae6e76 --- /dev/null +++ b/cmd/clean/main.go @@ -0,0 +1,119 @@ +/* +Copyright © 2025 Docker, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package main + +import ( + "flag" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/docker/mcp-registry/pkg/servers" +) + +// main processes the provided server names and cleans build artifacts for each. +func main() { + flag.Parse() + + if flag.NArg() == 0 { + fmt.Fprintln(os.Stderr, "Usage: task clean -- [server...]") + os.Exit(1) + } + + var failed bool + for _, name := range flag.Args() { + if err := cleanServer(name); err != nil { + fmt.Fprintf(os.Stderr, "cleanup failed for %s: %v\n", name, err) + failed = true + } + } + + if failed { + os.Exit(1) + } +} + +// cleanServer removes generated artifacts and Docker images for the server. +func cleanServer(name string) error { + serverPath := filepath.Join("servers", name, "server.yaml") + server, err := servers.Read(serverPath) + if err != nil { + return fmt.Errorf("reading server file: %w", err) + } + + removeCatalog(name) + removeDockerImage(server.Image) + removeDockerImage("check") + pruneDockerBuilder() + pruneDockerImages() + + return nil +} + +// removeCatalog deletes the generated catalog directory if it exists. +func removeCatalog(name string) { + path := filepath.Join("catalogs", name) + if err := os.RemoveAll(path); err != nil { + fmt.Fprintf(os.Stderr, "warning: removing %s: %v\n", path, err) + } +} + +// removeDockerImage removes the specified Docker image, ignoring missing images. +func removeDockerImage(image string) { + if image == "" { + return + } + + out, err := exec.Command("docker", "image", "rm", "-f", image).CombinedOutput() + if err != nil { + msg := string(out) + if strings.Contains(msg, "No such image") { + return + } + fmt.Fprintf(os.Stderr, "warning: removing image %s: %v\n%s", image, err, msg) + } else { + fmt.Print(string(out)) + } +} + +// pruneDockerBuilder removes unused builder cache entries. +func pruneDockerBuilder() { + cmd := exec.Command("docker", "builder", "prune", "--force") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "warning: pruning builder cache: %v\n", err) + } +} + +// pruneDockerImages removes dangling Docker images. +func pruneDockerImages() { + cmd := exec.Command("docker", "image", "prune", "--force") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "warning: pruning images: %v\n", err) + } +} diff --git a/cmd/create/main.go b/cmd/create/main.go index fb437a47..c3460fb9 100644 --- a/cmd/create/main.go +++ b/cmd/create/main.go @@ -128,10 +128,7 @@ func run(ctx context.Context, buildURL, name, category, userProvidedImage string } if build && userProvidedImage == "" { - gitURL := projectURL + ".git#" - if branch != "" { - gitURL += branch - } + gitURL := projectURL + ".git#" + sha if directory != "" && directory != "." { gitURL += ":" + directory } @@ -219,6 +216,7 @@ func run(ctx context.Context, buildURL, name, category, userProvidedImage string Project: projectURL, Upstream: upstream, Branch: branch, + Commit: sha, Directory: directory, }, Run: servers.Run{ diff --git a/cmd/remote-wizard/main.go b/cmd/remote-wizard/main.go new file mode 100644 index 00000000..8359e569 --- /dev/null +++ b/cmd/remote-wizard/main.go @@ -0,0 +1,302 @@ +package main + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strings" + + "github.com/charmbracelet/huh" + "github.com/charmbracelet/lipgloss" + "github.com/docker/mcp-registry/pkg/servers" + "gopkg.in/yaml.v3" +) + +var ( + transportTypes = []string{ + "streamable-http", + "sse", + } + + titleStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("#4ECDC4")). + Bold(true). + Padding(1, 2) + + headerStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("#4ECDC4")). + Bold(true). + Margin(1, 0). + Padding(1, 4) +) + +type RemoteWizardData struct { + ServerName string + Category string + Title string + Description string + Icon string + TransportType string + URL string + DocsURL string + UseOAuth bool +} + +func main() { + fmt.Print(titleStyle.Render("🐳 MCP Remote Server Registry Wizard")) + fmt.Print(headerStyle.Render("Welcome! Let's add your remote MCP server to the registry.")) + fmt.Println() + fmt.Println() + + var data RemoteWizardData + + // Basic Information Form + basicForm := huh.NewForm( + huh.NewGroup( + huh.NewInput(). + Title("Server Name"). + Description("Enter the name for your MCP server (e.g., 'my-awesome-server')"). + Value(&data.ServerName). + Validate(func(s string) error { + if strings.TrimSpace(s) == "" { + return fmt.Errorf("server name is required") + } + if strings.Contains(s, " ") { + return fmt.Errorf("server name cannot contain spaces") + } + exists, err := checkLocalServerExists(s) + if err != nil { + return err + } + if exists { + return fmt.Errorf("server name %s already exists", s) + } + return nil + }), + + huh.NewInput(). + Title("Category"). + Description("Enter the category that best describes your MCP server\n\t\t(e.g., ai, database, devops, productivity, search, communication, etc.)"). + Value(&data.Category). + Validate(func(s string) error { + if strings.TrimSpace(s) == "" { + return fmt.Errorf("category is required") + } + return nil + }), + ).Title("📋 Basic Information"), + ).WithTheme(huh.ThemeCharm()) + + if err := basicForm.Run(); err != nil { + log.Fatal(err) + } + + // Server Details Form + detailsForm := huh.NewForm( + huh.NewGroup( + huh.NewInput(). + Title("Server Title"). + Description("Enter a descriptive title for your MCP server"). + Value(&data.Title). + Validate(func(s string) error { + if strings.TrimSpace(s) == "" { + return fmt.Errorf("title is required") + } + return nil + }), + + huh.NewText(). + Title("Description"). + Description("Enter a detailed description of what your MCP server does"). + Value(&data.Description). + Validate(func(s string) error { + if strings.TrimSpace(s) == "" { + return fmt.Errorf("description is required") + } + return nil + }), + + huh.NewInput(). + Title("Icon URL"). + Description("Enter an icon URL (e.g., https://example.com/icon.png or use Google's favicon service)"). + Value(&data.Icon). + Validate(func(s string) error { + if strings.TrimSpace(s) == "" { + return fmt.Errorf("icon URL is required") + } + return nil + }), + + huh.NewInput(). + Title("Documentation URL"). + Description("Enter the URL to your server's documentation (will be saved in readme.md)"). + Value(&data.DocsURL). + Validate(func(s string) error { + if strings.TrimSpace(s) == "" { + return fmt.Errorf("documentation URL is required") + } + if !strings.HasPrefix(s, "http://") && !strings.HasPrefix(s, "https://") { + return fmt.Errorf("URL must start with http:// or https://") + } + return nil + }), + ).Title("📝 Server Details"), + ).WithTheme(huh.ThemeCharm()) + + if err := detailsForm.Run(); err != nil { + log.Fatal(err) + } + + // Remote Configuration Form + remoteForm := huh.NewForm( + huh.NewGroup( + huh.NewSelect[string](). + Title("Transport Type"). + Description("Select the transport protocol your remote server uses"). + Options(huh.NewOptions(transportTypes...)...). + Value(&data.TransportType), + + huh.NewInput(). + Title("Server URL"). + Description("Enter the full URL of your remote MCP server (e.g., https://mcp.example.com/mcp)"). + Value(&data.URL). + Validate(func(s string) error { + if strings.TrimSpace(s) == "" { + return fmt.Errorf("server URL is required") + } + if !strings.HasPrefix(s, "http://") && !strings.HasPrefix(s, "https://") { + return fmt.Errorf("URL must start with http:// or https://") + } + return nil + }), + ).Title("🌐 Remote Configuration"), + ).WithTheme(huh.ThemeCharm()) + + if err := remoteForm.Run(); err != nil { + log.Fatal(err) + } + + // OAuth Configuration Form + oauthForm := huh.NewForm( + huh.NewGroup( + huh.NewConfirm(). + Title("Use OAuth?"). + Description("Does your server require OAuth authentication?"). + Value(&data.UseOAuth), + ).Title("🔐 Authentication"), + ).WithTheme(huh.ThemeCharm()) + + if err := oauthForm.Run(); err != nil { + log.Fatal(err) + } + + // Generate and save the configuration + if err := generateAndSave(&data); err != nil { + log.Fatal(err) + } + + fmt.Print(headerStyle.Render("✅ Success! Your remote MCP server configuration has been generated.")) + fmt.Println() + fmt.Printf("📁 Generated files in servers/%s/:\n", data.ServerName) + fmt.Printf(" - server.yaml (server configuration)\n") + fmt.Printf(" - tools.json (empty, for dynamic tool discovery)\n") + fmt.Printf(" - readme.md (documentation link)\n") + fmt.Println() + fmt.Println("🚀 Next steps:") + fmt.Println("1. Review the generated server.yaml file") + fmt.Println("2. Test your server:") + fmt.Println(" task catalog -- " + data.ServerName) + fmt.Println(" docker mcp catalog import $PWD/catalogs/" + data.ServerName + "/catalog.yaml") + fmt.Println(" docker mcp server enable " + data.ServerName) + if data.UseOAuth { + fmt.Println(" docker mcp oauth authorize " + data.ServerName + " (for OAuth)") + } + fmt.Println("3. Reset catalog when done: docker mcp catalog reset") + fmt.Println("4. Create a pull request to add it to the registry") +} + +func generateAndSave(data *RemoteWizardData) error { + // Build the server configuration + config := servers.Server{ + Name: data.ServerName, + Type: "remote", + Dynamic: &servers.Dynamic{ + Tools: true, + }, + Meta: servers.Meta{ + Category: data.Category, + Tags: []string{data.Category, "remote"}, + }, + About: servers.About{ + Title: data.Title, + Description: data.Description, + Icon: data.Icon, + }, + Remote: servers.Remote{ + TransportType: data.TransportType, + URL: data.URL, + }, + } + + // Add OAuth configuration if needed + if data.UseOAuth { + config.OAuth = []servers.OAuthProvider{ + { + Provider: data.ServerName, + Secret: fmt.Sprintf("%s.personal_access_token", data.ServerName), + Env: fmt.Sprintf("%s_PERSONAL_ACCESS_TOKEN", strings.ToUpper(strings.ReplaceAll(data.ServerName, "-", "_"))), + }, + } + } + + // Create directory + serverDir := filepath.Join("servers", data.ServerName) + if err := os.MkdirAll(serverDir, 0755); err != nil { + return fmt.Errorf("failed to create directory: %w", err) + } + + // Marshal to YAML + yamlData, err := yaml.Marshal(&config) + if err != nil { + return fmt.Errorf("failed to marshal YAML: %w", err) + } + + // Write server.yaml file + configPath := filepath.Join(serverDir, "server.yaml") + if err := os.WriteFile(configPath, yamlData, 0644); err != nil { + return fmt.Errorf("failed to write config file: %w", err) + } + + // Create empty tools.json file (always empty for remote servers with dynamic tools) + toolsPath := filepath.Join(serverDir, "tools.json") + if err := os.WriteFile(toolsPath, []byte("[]"), 0644); err != nil { + return fmt.Errorf("failed to write tools file: %w", err) + } + + // Create readme.md file with documentation link + readmePath := filepath.Join(serverDir, "readme.md") + readmeContent := fmt.Sprintf("Docs: %s\n", data.DocsURL) + if err := os.WriteFile(readmePath, []byte(readmeContent), 0644); err != nil { + return fmt.Errorf("failed to write readme file: %w", err) + } + + return nil +} + +func checkLocalServerExists(name string) (bool, error) { + entries, err := os.ReadDir("servers") + if err != nil { + return false, err + } + + for _, entry := range entries { + if entry.IsDir() { + if entry.Name() == name { + return true, nil + } + } + } + + return false, nil +} diff --git a/cmd/security-reviewer/main.go b/cmd/security-reviewer/main.go new file mode 100644 index 00000000..1e85c3f7 --- /dev/null +++ b/cmd/security-reviewer/main.go @@ -0,0 +1,458 @@ +package main + +import ( + "context" + "errors" + "fmt" + "io" + "os" + "os/exec" + "os/signal" + "path/filepath" + "regexp" + "strings" + "syscall" + "time" + + "github.com/spf13/cobra" +) + +const ( + // composeFileName is the compose manifest executed for each review run. + composeFileName = "compose.yml" + // reportFileName is the name of the individual report emitted by the agent. + reportFileName = "report.md" + // labelsFileName is the name of the label output emitted by the agent. + labelsFileName = "labels.txt" + // repositoryDirName is the working directory used to stage repository clones. + repositoryDirName = "repository" + // dockerExecutable identifies the docker CLI binary invoked by the tool. + dockerExecutable = "docker" + // gitExecutable identifies the git CLI binary used to manage repositories. + gitExecutable = "git" + // projectPrefix is applied to compose project names to make them unique yet readable. + projectPrefix = "security-reviewer" + // agentService is the compose service name running the security reviewer container. + agentService = "reviewer" + // composeRelativePath is the path to the compose project relative to the repository root. + composeRelativePath = "agents/security-reviewer" + // defaultTimeoutSeconds bounds agent execution time when not explicitly configured. + defaultTimeoutSeconds = 3600 + + // envAnthropicAPIKey is the environment variable supplying Claude credentials. + envAnthropicAPIKey = "ANTHROPIC_API_KEY" + // envOpenAIAPIKey is the environment variable supplying Codex credentials. + envOpenAIAPIKey = "OPENAI_API_KEY" + + // agentNameClaude identifies the Claude-based reviewer. + agentNameClaude = "claude" + // agentNameCodex identifies the Codex-based reviewer. + agentNameCodex = "codex" +) + +// options stores parsed CLI arguments. +type options struct { + // Agent selects the underlying reviewer agent implementation. + Agent string + // Repository is the Git repository URL or filesystem path. + Repository string + // HeadSHA is the commit under audit. + HeadSHA string + // BaseSHA is the comparison commit for differential reviews. + BaseSHA string + // TargetLabel is an optional human friendly descriptor. + TargetLabel string + // OutputPath is the destination for the final report. + OutputPath string + // LabelsOutput is the destination for the label list produced by the reviewer. + LabelsOutput string + // Model optionally overrides the reviewer model selection. + Model string + // ExtraArgs optionally appends raw arguments to the agent CLI. + ExtraArgs string + // KeepWorkdir preserves the temporary workspace when true. + KeepWorkdir bool + // TimeoutSeconds bounds the reviewer runtime; zero uses the default. + TimeoutSeconds int +} + +var cliOpts = options{ + Agent: agentNameClaude, + OutputPath: "security-review.md", + TimeoutSeconds: defaultTimeoutSeconds, +} + +var rootCmd = &cobra.Command{ + Use: "security-reviewer", + Short: "Run the security reviewer compose workflow", + RunE: func(cmd *cobra.Command, args []string) error { + agent := strings.ToLower(strings.TrimSpace(cliOpts.Agent)) + if agent == "" { + agent = agentNameClaude + } + if agent != agentNameClaude && agent != agentNameCodex { + return fmt.Errorf("invalid agent %q (supported: %s, %s)", cliOpts.Agent, agentNameClaude, agentNameCodex) + } + + labelsOutput := strings.TrimSpace(cliOpts.LabelsOutput) + if labelsOutput == "" { + labelsOutput = deriveDefaultLabelsPath(cliOpts.OutputPath) + } + timeoutSecs := cliOpts.TimeoutSeconds + if timeoutSecs <= 0 { + timeoutSecs = defaultTimeoutSeconds + } + + opts := options{ + Agent: agent, + Repository: strings.TrimSpace(cliOpts.Repository), + HeadSHA: strings.TrimSpace(cliOpts.HeadSHA), + BaseSHA: strings.TrimSpace(cliOpts.BaseSHA), + TargetLabel: strings.TrimSpace(cliOpts.TargetLabel), + OutputPath: strings.TrimSpace(cliOpts.OutputPath), + LabelsOutput: labelsOutput, + Model: strings.TrimSpace(cliOpts.Model), + ExtraArgs: strings.TrimSpace(cliOpts.ExtraArgs), + KeepWorkdir: cliOpts.KeepWorkdir, + TimeoutSeconds: timeoutSecs, + } + + if opts.Repository == "" { + return errors.New("--repo is required") + } + if opts.HeadSHA == "" { + return errors.New("--head is required") + } + ctx := cmd.Context() + return run(ctx, opts) + }, +} + +func init() { + rootCmd.Flags().StringVar(&cliOpts.Agent, "agent", cliOpts.Agent, "Reviewer agent to use (claude or codex).") + rootCmd.Flags().StringVar(&cliOpts.Repository, "repo", cliOpts.Repository, "Git repository URL or local path to review.") + rootCmd.Flags().StringVar(&cliOpts.HeadSHA, "head", cliOpts.HeadSHA, "Head commit SHA to review.") + rootCmd.Flags().StringVar(&cliOpts.BaseSHA, "base", cliOpts.BaseSHA, "Base commit SHA for differential reviews.") + rootCmd.Flags().StringVar(&cliOpts.TargetLabel, "target-label", cliOpts.TargetLabel, "Human readable identifier for the target.") + rootCmd.Flags().StringVar(&cliOpts.OutputPath, "output", cliOpts.OutputPath, "Destination for the rendered report.") + rootCmd.Flags().StringVar(&cliOpts.LabelsOutput, "labels-output", cliOpts.LabelsOutput, "Destination for the labels file (defaults alongside the report).") + rootCmd.Flags().IntVar(&cliOpts.TimeoutSeconds, "timeout", cliOpts.TimeoutSeconds, "Maximum runtime for the review in seconds (defaults to 3600 seconds).") + rootCmd.Flags().StringVar(&cliOpts.Model, "model", cliOpts.Model, "Override the reviewer model for the selected agent.") + rootCmd.Flags().StringVar(&cliOpts.ExtraArgs, "extra-args", cliOpts.ExtraArgs, "Additional arguments passed to the reviewer agent.") + rootCmd.Flags().BoolVar(&cliOpts.KeepWorkdir, "keep-workdir", cliOpts.KeepWorkdir, "Keep the temporary workspace after completion.") + + _ = rootCmd.MarkFlagRequired("repo") + _ = rootCmd.MarkFlagRequired("head") +} + +// main is the entry point for the security reviewer CLI. +func main() { + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + rootCmd.SilenceUsage = true + if err := rootCmd.ExecuteContext(ctx); err != nil { + exitWithError(err) + } +} + +// run coordinates workspace preparation, compose execution, and cleanup. +func run(ctx context.Context, opts options) error { + if opts.BaseSHA != "" { + fmt.Printf( + "Starting differential security review (agent=%s head=%s base=%s target=%s)\n", + opts.Agent, + opts.HeadSHA, + opts.BaseSHA, + opts.TargetLabel, + ) + } else { + fmt.Printf( + "Starting full security review (agent=%s head=%s target=%s)\n", + opts.Agent, + opts.HeadSHA, + opts.TargetLabel, + ) + } + + // Ensure the credential proxy has the API keys it needs before staging work. + switch opts.Agent { + case "claude": + if _, ok := os.LookupEnv(envAnthropicAPIKey); !ok { + return errors.New("ANTHROPIC_API_KEY environment variable is required for the Claude agent") + } + case "codex": + if _, ok := os.LookupEnv(envOpenAIAPIKey); !ok { + return errors.New("OPENAI_API_KEY environment variable is required for the Codex agent") + } + } + + // Prepare a temporary workspace to stage inputs and outputs. + workdir, err := os.MkdirTemp("", fmt.Sprintf("security-reviewer-%s-", opts.Agent)) + if err != nil { + return fmt.Errorf("create temporary directory: %w", err) + } + + if !opts.KeepWorkdir { + defer os.RemoveAll(workdir) + } else { + fmt.Printf("Temporary workspace preserved at %s\n", workdir) + } + + // Materialize the repository commits required for the review. + repositoryDir := filepath.Join(workdir, repositoryDirName) + if err = prepareRepository(ctx, opts, repositoryDir); err != nil { + return err + } + + outputDir := filepath.Join(workdir, "output") + if err = os.MkdirAll(outputDir, 0o755); err != nil { + return fmt.Errorf("create output directory: %w", err) + } + + // Launch the compose project and wait for the reviewer to finish. + if err = runCompose(ctx, opts, workdir, repositoryDir, outputDir); err != nil { + return err + } + + // Copy the generated artifacts back to the requested destinations. + reportPath := filepath.Join(outputDir, reportFileName) + labelsPath := filepath.Join(outputDir, labelsFileName) + if _, err = os.Stat(reportPath); err != nil { + return fmt.Errorf("review report not produced: %w", err) + } + if _, err = os.Stat(labelsPath); err != nil { + return fmt.Errorf("labels file not produced: %w", err) + } + + if err = copyFile(reportPath, opts.OutputPath); err != nil { + return err + } + if err = copyFile(labelsPath, opts.LabelsOutput); err != nil { + return err + } + + fmt.Printf("Security review report copied to %s\n", opts.OutputPath) + fmt.Printf("Security review labels copied to %s\n", opts.LabelsOutput) + return nil +} + +// deriveDefaultLabelsPath produces a labels output path near the report path. +func deriveDefaultLabelsPath(reportPath string) string { + reportPath = strings.TrimSpace(reportPath) + if reportPath == "" { + // Without an explicit report, fall back to a stable default name. + return "security-review-labels.txt" + } + // Place the labels file alongside the report for easier discovery. + dir := filepath.Dir(reportPath) + base := filepath.Base(reportPath) + idx := strings.LastIndex(base, ".") + if idx > 0 { + // Drop the extension so the generated labels file mirrors the report name. + base = base[:idx] + } + if strings.TrimSpace(base) == "" { + base = "security-review" + } + // Append a suffix to distinguish the labels artifact from the report. + return filepath.Join(dir, base+"-labels.txt") +} + +// sanitizeName converts arbitrary text into a slug. +func sanitizeName(text string) string { + lower := strings.ToLower(text) + pattern := regexp.MustCompile(`[^a-z0-9]+`) + cleaned := pattern.ReplaceAllString(lower, "-") + trimmed := strings.Trim(cleaned, "-") + if trimmed == "" { + return "target" + } + return trimmed +} + +// prepareRepository clones the repository and materializes commits for review. +func prepareRepository(ctx context.Context, opts options, repositoryDir string) error { + parentDir := filepath.Dir(repositoryDir) + if err := os.MkdirAll(parentDir, 0o755); err != nil { + return fmt.Errorf("create repository parent directory: %w", err) + } + if err := os.RemoveAll(repositoryDir); err != nil { + return fmt.Errorf("reset repository directory: %w", err) + } + + if err := runCommand(ctx, "", gitExecutable, "clone", opts.Repository, repositoryDir); err != nil { + return fmt.Errorf("clone repository: %w", err) + } + + if err := ensureCommit(ctx, repositoryDir, opts.HeadSHA); err != nil { + return err + } + if err := runCommand(ctx, repositoryDir, gitExecutable, "checkout", "--detach", opts.HeadSHA); err != nil { + return fmt.Errorf("checkout head commit: %w", err) + } + + if opts.BaseSHA != "" { + if err := ensureCommit(ctx, repositoryDir, opts.BaseSHA); err != nil { + return err + } + } + + return nil +} + +// ensureCommit verifies that a commit exists locally, fetching if needed. +func ensureCommit(ctx context.Context, repoDir, sha string) error { + if sha == "" { + return nil + } + if err := runCommand(ctx, repoDir, gitExecutable, "rev-parse", "--verify", sha); err == nil { + return nil + } + if err := runCommand(ctx, repoDir, gitExecutable, "fetch", "origin", sha); err != nil { + return fmt.Errorf("fetch commit %s: %w", sha, err) + } + if err := runCommand(ctx, repoDir, gitExecutable, "rev-parse", "--verify", sha); err != nil { + return fmt.Errorf("verify commit %s: %w", sha, err) + } + return nil +} + +// copyFile copies a file from src to dst, creating parent directories. +func copyFile(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return fmt.Errorf("open file %s: %w", src, err) + } + defer in.Close() + if err = os.MkdirAll(filepath.Dir(dst), 0o755); err != nil { + return fmt.Errorf("create directory for %s: %w", dst, err) + } + out, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644) + if err != nil { + return fmt.Errorf("open destination %s: %w", dst, err) + } + defer out.Close() + if _, err = io.Copy(out, in); err != nil { + return fmt.Errorf("copy %s to %s: %w", src, dst, err) + } + return nil +} + +// runCompose executes the docker compose workflow for the review. +func runCompose(ctx context.Context, opts options, workdir, repositoryDir, outputDir string) error { + // Compose assumes relative paths, so stage a copy inside the temp workspace. + composeDir := filepath.Join(workdir, composeRelativePath) + if err := copyDir(composeRelativePath, composeDir); err != nil { + return err + } + + env := buildComposeEnv(opts, repositoryDir, outputDir) + up := exec.CommandContext(ctx, dockerExecutable, "compose", "-f", composeFileName, "up", "--build", "--abort-on-container-exit", "--exit-code-from", agentService) + up.Dir = composeDir + up.Env = env + up.Stdout = os.Stdout + up.Stderr = os.Stderr + + down := exec.CommandContext(context.Background(), dockerExecutable, "compose", "-f", composeFileName, "down", "--volumes", "--remove-orphans") + down.Dir = composeDir + down.Env = env + + if err := up.Run(); err != nil { + _ = down.Run() + return fmt.Errorf("docker compose up: %w", err) + } + if err := down.Run(); err != nil { + return fmt.Errorf("docker compose down: %w", err) + } + return nil +} + +// buildComposeEnv prepares environment variables for docker compose. +func buildComposeEnv(opts options, repositoryDir, outputDir string) []string { + env := os.Environ() + // Generate a stable slug to keep compose project names readable. + slug := sanitizeName(opts.TargetLabel) + if slug == "target" { + repoBase := filepath.Base(strings.TrimSuffix(opts.Repository, ".git")) + slug = sanitizeName(repoBase) + } + if slug == "" { + slug = "target" + } + projectName := fmt.Sprintf("%s-%s-%d", projectPrefix, slug, time.Now().Unix()) + + // Get current UID/GID to match container user with host user. + // This avoids permission issues with bind mounts. + uid, gid := getCurrentUIDGID() + + env = append(env, + fmt.Sprintf("COMPOSE_PROJECT_NAME=%s", projectName), + fmt.Sprintf("REVIEW_AGENT=%s", opts.Agent), + fmt.Sprintf("REVIEW_HEAD_SHA=%s", opts.HeadSHA), + fmt.Sprintf("REVIEW_BASE_SHA=%s", opts.BaseSHA), + fmt.Sprintf("REVIEW_TARGET_LABEL=%s", opts.TargetLabel), + fmt.Sprintf("REVIEW_REPOSITORY_PATH=%s", repositoryDir), + fmt.Sprintf("REVIEW_OUTPUT_PATH=%s", outputDir), + fmt.Sprintf("REVIEW_TIMEOUT_SECS=%d", opts.TimeoutSeconds), + fmt.Sprintf("AGENT_UID=%d", uid), + fmt.Sprintf("AGENT_GID=%d", gid), + ) + if opts.Model != "" { + // Route custom models to the right environment variable per agent. + switch strings.ToLower(opts.Agent) { + case agentNameClaude: + env = append(env, fmt.Sprintf("CLAUDE_REVIEW_MODEL=%s", opts.Model)) + case agentNameCodex: + env = append(env, fmt.Sprintf("CODEX_REVIEW_MODEL=%s", opts.Model)) + } + } + if opts.ExtraArgs != "" { + env = append(env, fmt.Sprintf("REVIEW_AGENT_EXTRA_ARGS=%s", opts.ExtraArgs)) + } + if key := strings.TrimSpace(os.Getenv(envAnthropicAPIKey)); key != "" { + env = append(env, fmt.Sprintf("%s=%s", envAnthropicAPIKey, key)) + } + if key := strings.TrimSpace(os.Getenv(envOpenAIAPIKey)); key != "" { + env = append(env, fmt.Sprintf("%s=%s", envOpenAIAPIKey, key)) + } + return env +} + +// copyDir performs a recursive directory copy. +func copyDir(src, dst string) error { + return filepath.Walk(src, func(path string, info os.FileInfo, walkErr error) error { + if walkErr != nil { + return walkErr + } + rel, err := filepath.Rel(src, path) + if err != nil { + return err + } + target := filepath.Join(dst, rel) + if info.IsDir() { + return os.MkdirAll(target, info.Mode()) + } + data, err := os.ReadFile(path) + if err != nil { + return err + } + return os.WriteFile(target, data, info.Mode()) + }) +} + +// runCommand executes a command within an optional directory. +func runCommand(ctx context.Context, dir, name string, args ...string) error { + cmd := exec.CommandContext(ctx, name, args...) + if dir != "" { + cmd.Dir = dir + } + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + +// exitWithError prints an error and terminates the process. +func exitWithError(err error) { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) +} diff --git a/cmd/security-reviewer/uidgid_linux.go b/cmd/security-reviewer/uidgid_linux.go new file mode 100644 index 00000000..682207b2 --- /dev/null +++ b/cmd/security-reviewer/uidgid_linux.go @@ -0,0 +1,32 @@ +/* +Copyright © 2025 Docker, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +//go:build linux + +package main + +import "os" + +// getCurrentUIDGID returns the current process's UID and GID. +func getCurrentUIDGID() (uid, gid int) { + return os.Getuid(), os.Getgid() +} diff --git a/cmd/security-reviewer/uidgid_other.go b/cmd/security-reviewer/uidgid_other.go new file mode 100644 index 00000000..f1a33aad --- /dev/null +++ b/cmd/security-reviewer/uidgid_other.go @@ -0,0 +1,32 @@ +/* +Copyright © 2025 Docker, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +//go:build !linux + +package main + +// getCurrentUIDGID returns sensible default UID/GID values. +// On non-Linux systems (macOS, Windows), Docker Desktop handles UID mapping +// automatically, so we use the default values that match the Dockerfile. +func getCurrentUIDGID() (uid, gid int) { + return 1000, 1000 +} diff --git a/cmd/validate/main.go b/cmd/validate/main.go index 1e0459da..bcfde595 100644 --- a/cmd/validate/main.go +++ b/cmd/validate/main.go @@ -43,6 +43,10 @@ func run(name string) error { return err } + if err := isCommitPinnedIfNecessary(name); err != nil { + return err + } + if err := areSecretsValid(name); err != nil { return err } @@ -72,10 +76,21 @@ func run(name string) error { return nil } +// legacyNameExceptions enumerates catalog entries added before current naming rules. +var legacyNameExceptions = map[string]bool{ + "SQLite": true, + "osp_marketing_tools": true, + "youtube_transcript": true, +} + // check if the name is a valid func isNameValid(name string) error { // check if name has only letters, numbers, and hyphens if !regexp.MustCompile(`^[a-z0-9-]+$`).MatchString(name) { + if legacyNameExceptions[name] { + fmt.Printf("⚠️ Name %s is grandfathered and bypasses naming rules.\n", name) + return nil + } return fmt.Errorf("name is not valid. It must be a lowercase string with only letters, numbers, and hyphens") } @@ -104,21 +119,63 @@ func isDirectoryValid(name string) error { return nil } +var commitSHA1Pattern = regexp.MustCompile(`^[a-f0-9]{40}$`) + +// isCommitPinnedIfNecessary ensures that every local server is pinned to a specific commit. +func isCommitPinnedIfNecessary(name string) error { + server, err := readServerYaml(name) + if err != nil { + return err + } + + if server.Type != "server" { + fmt.Println("✅ Commit pin not required (non-local server)") + return nil + } + + if server.Source.Commit == "" { + return fmt.Errorf("local server must specify source.commit to pin the audited revision") + } + + if !commitSHA1Pattern.MatchString(strings.ToLower(server.Source.Commit)) { + return fmt.Errorf("source.commit must be a 40-character lowercase SHA1 (got %q)", server.Source.Commit) + } + + fmt.Println("✅ Commit is pinned") + return nil +} + +// secretNamePattern validates that secret names match the expected prefix.name +// format requirement. +var secretNamePattern = regexp.MustCompile(`^[A-Za-z0-9_-]+\.[A-Za-z0-9._-]+$`) + +// legacySecretNameExceptions enumerates secrets defined before the current +// naming rules were introduced. +var legacySecretNameExceptions = map[string]map[string]bool{ + "nasdaq-data-link": { + "nasdaq_data_link_api_key": true, + }, + "sec-edgar": { + "sec_edgar_user_agent": true, + }, +} + // check if the secrets are valid -// secrets must be prefixed with the name of the server func areSecretsValid(name string) error { - // read the server.yaml file server, err := readServerYaml(name) if err != nil { return err } - // check if the server.yaml file has a valid secrets - if len(server.Config.Secrets) > 0 { - for _, secret := range server.Config.Secrets { - if !strings.HasPrefix(secret.Name, name+".") { - return fmt.Errorf("secret %s is not valid. It must be prefixed with the name of the server", secret.Name) + // Ensure that all secrets match the expected format. We no longer require + // that the prefix matches the server name. + for _, secret := range server.Config.Secrets { + if !secretNamePattern.MatchString(secret.Name) { + if legacySecretNameExceptions[name][secret.Name] { + fmt.Printf("⚠️ Secret %s for %s is grandfathered and bypasses naming rules.\n", secret.Name, name) + continue } + return fmt.Errorf("secret %s is not valid. It must use prefix.name format with alphanumeric characters, hyphen, period, or underscore", secret.Name) } } @@ -182,44 +239,51 @@ func isIconValid(name string) error { } if server.About.Icon == "" { - fmt.Println("🛑 No icon found") + fmt.Println("⚠️ No icon found") return nil } // fetch the image and check the size resp, err := http.Get(server.About.Icon) if err != nil { - fmt.Println("🛑 Icon could not be fetched") + fmt.Println("⚠️ Icon could not be fetched") return nil } defer resp.Body.Close() if resp.StatusCode != 200 { - fmt.Printf("🛑 Icon could not be fetched, status code: %d, url: %s\n", resp.StatusCode, server.About.Icon) + fmt.Printf("⚠️ Icon could not be fetched, status code: %d, url: %s\n", resp.StatusCode, server.About.Icon) return nil } if resp.ContentLength > 2*1024*1024 { - fmt.Println("🛑 Icon is too large. It must be less than 2MB") + fmt.Println("⚠️ Icon is too large. It must be less than 2MB") return nil } - // Check content type for SVG support + // Check content type for SVG, favicon, and WebP support contentType := resp.Header.Get("Content-Type") - if contentType == "image/svg+xml" { + switch contentType { + case "image/svg+xml": fmt.Println("✅ Icon is valid (SVG)") return nil + case "image/x-icon": + fmt.Println("✅ Icon is valid (favicon)") + return nil + case "image/webp": + fmt.Println("✅ Icon is valid (WebP)") + return nil } img, format, err := image.DecodeConfig(resp.Body) if err != nil { return err } - if format != "png" { - fmt.Println("🛑 Icon is not a png or svg. It must be a png or svg") + if format != "png" && format != "jpeg" { + fmt.Println("⚠️ Icon is not a png or svg. It must be a png or svg") return nil } if img.Width > 512 || img.Height > 512 { - fmt.Println("🛑 Icon is too large. It must be less than 512x512") + fmt.Println("⚠️ Icon is too large. It must be less than 512x512") return nil } @@ -304,6 +368,11 @@ func hasValidTools(server servers.Server) error { return nil } +// Some special entries bypass the dynamic tools requirement. +var oauthDynamicToolExceptions = map[string]bool{ + "github-official": true, +} + // check if servers with OAuth have dynamic tools enabled func isOAuthDynamicValid(name string) error { server, err := readServerYaml(name) @@ -314,7 +383,11 @@ func isOAuthDynamicValid(name string) error { // If server has OAuth configuration, it must have dynamic tools enabled if len(server.OAuth) > 0 { if server.Dynamic == nil || !server.Dynamic.Tools { - return fmt.Errorf("server with OAuth must have 'dynamic: tools: true' configuration") + if oauthDynamicToolExceptions[name] { + fmt.Printf("⚠️ OAuth dynamic rule bypassed for %s (special configuration).\n", name) + } else { + return fmt.Errorf("server with OAuth must have 'dynamic: tools: true' configuration") + } } } diff --git a/cmd/validate/main_test.go b/cmd/validate/main_test.go index a66369be..bd32636e 100644 --- a/cmd/validate/main_test.go +++ b/cmd/validate/main_test.go @@ -1,9 +1,42 @@ package main import ( + "fmt" + "os" + "path/filepath" + "runtime" "testing" ) +func TestMain(m *testing.M) { + // Compute the path to this source code file. + _, thisFile, _, ok := runtime.Caller(0) + if !ok { + fmt.Fprintln(os.Stderr, "mcp-registry/cmd/validate: unable to resolve caller path") + os.Exit(1) + } + + // Switch to the repository root so that readServerYaml calls from tests can + // access YAML files. + repoRoot := filepath.Clean(filepath.Join(filepath.Dir(thisFile), "..", "..")) + if err := os.Chdir(repoRoot); err != nil { + fmt.Fprintln(os.Stderr, "mcp-registry/cmd/validate: chdir:", err) + os.Exit(1) + } + + // Run the tests in this package. + code := m.Run() + + // Restore the working directory. + originalWD := filepath.Clean(filepath.Join(repoRoot, "cmd", "validate")) + if err := os.Chdir(originalWD); err != nil { + fmt.Fprintln(os.Stderr, "mcp-registry/cmd/validate: restore chdir:", err) + os.Exit(1) + } + + os.Exit(code) +} + func Test_isNameValid(t *testing.T) { type args struct { name string @@ -55,6 +88,20 @@ func Test_isNameValid(t *testing.T) { }, wantError: true, }, + { + name: "legacy uppercase name", + args: args{ + name: "SQLite", + }, + wantError: false, + }, + { + name: "legacy underscore name", + args: args{ + name: "youtube_transcript", + }, + wantError: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/cmd/wizard/main.go b/cmd/wizard/main.go index b943105a..650d5ed9 100644 --- a/cmd/wizard/main.go +++ b/cmd/wizard/main.go @@ -87,6 +87,7 @@ type WizardData struct { ServerName string GitHubRepo string Branch string + Commit string Category string Title string Description string @@ -506,6 +507,7 @@ func generateAndSave(data *WizardData) error { }, Source: servers.Source{ Project: data.GitHubRepo, + Commit: data.Commit, }, } @@ -618,6 +620,14 @@ func validateGithubRepo(data *WizardData) error { return err } + sha, err := client.GetCommitSHA1(ctx, detectedInfo.ProjectURL, detectedInfo.Branch) + if err != nil { + return err + } + + data.GitHubRepo = detectedInfo.ProjectURL + data.Commit = sha + parts := strings.Split(strings.ToLower(data.GitHubRepo), "/") name := parts[len(parts)-1] diff --git a/docs/configuration.md b/docs/configuration.md index f49dd167..0705aab0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -85,6 +85,10 @@ run: - --transport=stdio ``` +## Source Pinning + +Local servers must pin their source repository to a specific Git commit using the `source.commit` field. Once an initial revision is accepted into the registry, an automated nightly GitHub Action will drive PRs to perform updates. + ## User If you need to run the container with a specific user, you can do it in the `run` block. If you want the user to be able to define the container user, you will need to create a parameter first and then add the `run` block to the server. @@ -119,6 +123,7 @@ about: icon: https://... source: project: https://github.com/my-org/my-mcp-server + commit: 0123456789abcdef0123456789abcdef01234567 run: command: - --transport=stdio diff --git a/go.mod b/go.mod index 32f974ad..ff589edb 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/charmbracelet/lipgloss v1.1.0 github.com/google/go-github/v70 v70.0.0 github.com/mark3labs/mcp-go v0.25.0 + github.com/spf13/cobra v1.8.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -25,6 +26,7 @@ require ( github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect @@ -35,6 +37,7 @@ require ( github.com/muesli/termenv v0.16.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect golang.org/x/sync v0.12.0 // indirect diff --git a/go.sum b/go.sum index 493a8b02..24ad4f3a 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,7 @@ github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8 github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= github.com/charmbracelet/x/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI= github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -55,6 +56,8 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -84,8 +87,13 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= diff --git a/pkg/catalog/types.go b/pkg/catalog/types.go index 64154253..23625040 100644 --- a/pkg/catalog/types.go +++ b/pkg/catalog/types.go @@ -207,38 +207,38 @@ type Remote struct { func (r Remote) MarshalYAML() (interface{}, error) { mapNode := &yaml.Node{ - Kind: yaml.MappingNode, + Kind: yaml.MappingNode, Content: []*yaml.Node{}, } - + if r.TransportType != "" { mapNode.Content = append(mapNode.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: "transport_type"}, &yaml.Node{Kind: yaml.ScalarNode, Value: r.TransportType}) } - + if r.URL != "" { mapNode.Content = append(mapNode.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: "url"}, &yaml.Node{Kind: yaml.ScalarNode, Value: r.URL}) } - + if len(r.Headers) > 0 { headersNode := &yaml.Node{ - Kind: yaml.MappingNode, + Kind: yaml.MappingNode, Content: []*yaml.Node{}, } - + for k, v := range r.Headers { headersNode.Content = append(headersNode.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: k}, &yaml.Node{Kind: yaml.ScalarNode, Value: v, Style: yaml.DoubleQuotedStyle}) } - + mapNode.Content = append(mapNode.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: "headers"}, headersNode) } - + return mapNode, nil } diff --git a/pkg/servers/server.go b/pkg/servers/server.go index 4d2ff5f6..23164b35 100644 --- a/pkg/servers/server.go +++ b/pkg/servers/server.go @@ -29,10 +29,16 @@ import ( func (s *Server) GetContext() string { base := s.Source.Project + ".git" - if s.GetBranch() != "main" { - base += "#" + s.Source.Branch + revision := s.Source.Commit + if revision != "" { + base += "#" + revision } else { - base += "#" + branch := s.GetBranch() + if branch != "main" { + base += "#" + branch + } else { + base += "#" + } } if s.Source.Directory != "" && s.Source.Directory != "." { @@ -43,7 +49,12 @@ func (s *Server) GetContext() string { } func (s *Server) GetSourceURL() string { - source := s.Source.Project + "/tree/" + s.GetBranch() + revision := s.Source.Commit + if revision == "" { + revision = s.GetBranch() + } + + source := s.Source.Project + "/tree/" + revision if s.Source.Directory != "" { source += "/" + s.Source.Directory } @@ -65,7 +76,12 @@ func (s *Server) GetBranch() string { } func (s *Server) GetDockerfileUrl() string { - base := s.Source.Project + "/blob/" + s.GetBranch() + revision := s.Source.Commit + if revision == "" { + revision = s.GetBranch() + } + + base := s.Source.Project + "/blob/" + revision if s.Source.Directory != "" { base += "/" + s.Source.Directory } diff --git a/pkg/servers/types.go b/pkg/servers/types.go index 9164ff74..20930985 100644 --- a/pkg/servers/types.go +++ b/pkg/servers/types.go @@ -105,6 +105,7 @@ type Source struct { Project string `yaml:"project,omitempty" json:"project,omitempty"` Upstream string `yaml:"upstream,omitempty" json:"upstream,omitempty"` Branch string `yaml:"branch,omitempty" json:"branch,omitempty"` + Commit string `yaml:"commit,omitempty" json:"commit,omitempty"` Directory string `yaml:"directory,omitempty" json:"directory,omitempty"` Dockerfile string `yaml:"dockerfile,omitempty" json:"dockerfile,omitempty"` BuildTarget string `yaml:"buildTarget,omitempty" json:"buildTarget,omitempty"` diff --git a/scripts/ci-validation.sh b/scripts/ci-validation.sh index 1b778140..b0b852dc 100755 --- a/scripts/ci-validation.sh +++ b/scripts/ci-validation.sh @@ -11,36 +11,42 @@ process_server() { local file="$1" local dir=$(dirname "$file") local name=$(basename "$dir") - + echo "Processing server: $name" echo "================================" - + # Run each command and check for failures if ! task validate -- --name "$name"; then echo "ERROR: Validation failed for $name" + task clean -- "$name" >/dev/null 2>&1 || true return 1 fi - + if ! task build -- --tools --pull-community "$name"; then echo "ERROR: Build failed for $name" + task clean -- "$name" >/dev/null 2>&1 || true return 1 fi - + echo "--------------------------------" - + if ! task catalog -- "$name"; then echo "ERROR: Catalog generation failed for $name" + task clean -- "$name" >/dev/null 2>&1 || true return 1 fi - + echo "--------------------------------" - + cat "catalogs/$name/catalog.yaml" - + echo "--------------------------------" echo "Successfully processed: $name" echo "" - + if ! task clean -- "$name"; then + echo "WARNING: Cleanup encountered issues for $name" + fi + return 0 } @@ -54,10 +60,10 @@ while IFS= read -r file; do echo "Skipping already processed server: $name (from file: $file)" continue fi - + # Mark this server as processed processed_servers="${processed_servers}|$name|" - + if ! process_server "$file"; then echo "FAILED: Processing server from file: $file" overall_success=false @@ -71,4 +77,4 @@ if [ "$overall_success" = true ]; then else echo "One or more servers failed to process!" exit 1 -fi \ No newline at end of file +fi diff --git a/servers/SQLite/server.yaml b/servers/SQLite/server.yaml index adf609cf..eec0c071 100644 --- a/servers/SQLite/server.yaml +++ b/servers/SQLite/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 directory: src/sqlite run: command: diff --git a/servers/airtable-mcp-server/server.yaml b/servers/airtable-mcp-server/server.yaml index deece6b1..1efb261f 100644 --- a/servers/airtable-mcp-server/server.yaml +++ b/servers/airtable-mcp-server/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/domdomegg/airtable-mcp-server branch: master + commit: 3b8770cae3a1ba18bf6aeac2f40095c12749bd92 config: description: Configure the connection to Airtable mcp server secrets: @@ -21,7 +22,7 @@ config: env: - name: NODE_ENV example: production - value: "{{airtable-mcp-server.node_env}}" + value: "{{airtable-mcp-server.nodeenv}}" parameters: type: object properties: diff --git a/servers/aks/server.yaml b/servers/aks/server.yaml index fb2777df..59d6bf11 100644 --- a/servers/aks/server.yaml +++ b/servers/aks/server.yaml @@ -11,9 +11,10 @@ meta: about: title: Azure Kubernetes Service (AKS) description: Azure Kubernetes Service (AKS) official MCP server - icon: https://raw.githubusercontent.com/Azure/AKS/master/blog/assets/images/400x400.png + icon: https://raw.githubusercontent.com/Azure/AKS/1c3979cf6729c78cfff2c0c78cb0aa657e1bbf7a/blog/assets/images/400x400.png source: project: https://github.com/Azure/aks-mcp + commit: 6c6d6600de5bfbbbe477407c311324cb7e0d78e7 run: command: - --transport=stdio diff --git a/servers/apify-mcp-server/server.yaml b/servers/apify-mcp-server/server.yaml index 7586659a..aab64512 100644 --- a/servers/apify-mcp-server/server.yaml +++ b/servers/apify-mcp-server/server.yaml @@ -1,6 +1,7 @@ name: apify-mcp-server image: mcp/apify-mcp-server type: server +longLived: true meta: category: productivity tags: @@ -18,6 +19,7 @@ about: source: project: https://github.com/apify/actors-mcp-server branch: master + commit: 4e3cf9de2ea4896665a200b07f2e785881a2da9b config: description: Configure the Apify MCP Server secrets: diff --git a/servers/apollo-mcp-server/server.yaml b/servers/apollo-mcp-server/server.yaml index 73874ab7..466f820b 100644 --- a/servers/apollo-mcp-server/server.yaml +++ b/servers/apollo-mcp-server/server.yaml @@ -11,6 +11,7 @@ about: icon: https://www.apollographql.com/favicon/android-icon-192x192.png source: project: https://github.com/apollographql/apollo-mcp-server + commit: f481f59105966975c04febbed9efc8b62a93ec32 config: description: Configure the connection to Apollo MCP Server env: diff --git a/servers/arxiv-mcp-server/server.yaml b/servers/arxiv-mcp-server/server.yaml index 89ece47c..850d4314 100644 --- a/servers/arxiv-mcp-server/server.yaml +++ b/servers/arxiv-mcp-server/server.yaml @@ -33,6 +33,7 @@ about: icon: https://avatars.githubusercontent.com/u/15390319?v=4 source: project: https://github.com/jasonleinart/arxiv-mcp-server + commit: fe977e538dc24bb2da937c6c9e48febb54083226 run: volumes: - '{{arxiv-mcp-server.storage_path}}:/app/papers' diff --git a/servers/asana/server.yaml b/servers/asana/server.yaml index 3ec7bd41..7eab656a 100644 --- a/servers/asana/server.yaml +++ b/servers/asana/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Asana - description: Project Management + description: Interact with your Asana workspace icon: https://www.google.com/s2/favicons?domain=asana.com&sz=64 remote: transport_type: sse diff --git a/servers/ast-grep/server.yaml b/servers/ast-grep/server.yaml index 13bf4536..2a0a6e4d 100644 --- a/servers/ast-grep/server.yaml +++ b/servers/ast-grep/server.yaml @@ -13,9 +13,11 @@ about: icon: https://avatars.githubusercontent.com/u/114017360?s=200&v=4 source: project: https://github.com/dgageot/mcp-ast-grep + commit: c0a1edaf9cd6b168f2b59bc5a0161ad49a793099 run: volumes: - '{{ast-grep.path|volume-target}}:/src' + disableNetwork: true config: description: The MCP server is allowed to access this directory parameters: diff --git a/servers/astra-db/server.yaml b/servers/astra-db/server.yaml index 27f086c7..1060116c 100644 --- a/servers/astra-db/server.yaml +++ b/servers/astra-db/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/573369?s=200&v=4 source: project: https://github.com/datastax/astra-db-mcp + commit: 3f39fac29c232809da9db57920d68757b5cba8f3 config: description: Configure the connection to Astra DB secrets: diff --git a/servers/atlan/server.yaml b/servers/atlan/server.yaml index 3365db8f..f232b03d 100644 --- a/servers/atlan/server.yaml +++ b/servers/atlan/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/atlanhq/agent-toolkit branch: main + commit: b78a61ff5a6fd24acf8950b49cf043f33333d271 directory: modelcontextprotocol config: description: Configure the connection to Atlan @@ -23,4 +24,4 @@ config: - name: atlan.atlan_base_url env: ATLAN_BASE_URL example: https://your-instance.atlan.com - \ No newline at end of file + diff --git a/servers/atlas-docs/server.yaml b/servers/atlas-docs/server.yaml index 89bdac38..2ae6305c 100644 --- a/servers/atlas-docs/server.yaml +++ b/servers/atlas-docs/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/CartographAI/atlas-docs-mcp branch: master + commit: ce4c4fe8ef23d0a9db4f04370b533ac4b9b172f8 config: description: Configure the connection to Atlas Docs env: diff --git a/servers/atlassian-remote/readme.md b/servers/atlassian-remote/readme.md deleted file mode 100644 index 70db551d..00000000 --- a/servers/atlassian-remote/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/ diff --git a/servers/atlassian-remote/server.yaml b/servers/atlassian-remote/server.yaml deleted file mode 100644 index f9cd181b..00000000 --- a/servers/atlassian-remote/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: atlassian-remote -type: remote -dynamic: - tools: true -meta: - category: devops - tags: - - devops - - software-development - - collaboration - - remote -about: - title: Atlassian - description: Software Development - icon: https://www.google.com/s2/favicons?domain=atlassian.com&sz=64 -remote: - transport_type: sse - url: https://mcp.atlassian.com/v1/sse -oauth: - - provider: atlassian-remote - secret: atlassian-remote.personal_access_token - env: ATLAS_PERSONAL_ACCESS_TOKEN diff --git a/servers/atlassian-remote/tools.json b/servers/atlassian-remote/tools.json deleted file mode 100644 index ee32261d..00000000 --- a/servers/atlassian-remote/tools.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "name": "search_jira_issues", - "description": "Search for Jira issues across projects", - "arguments": [] - }, - { - "name": "create_jira_issue", - "description": "Create a new Jira issue", - "arguments": [] - }, - { - "name": "search_confluence_pages", - "description": "Search for Confluence pages and content", - "arguments": [] - }, - { - "name": "create_confluence_page", - "description": "Create a new Confluence page", - "arguments": [] - }, - { - "name": "summarize_content", - "description": "Summarize content across Jira, Confluence, and Compass", - "arguments": [] - } -] \ No newline at end of file diff --git a/servers/atlassian/server.yaml b/servers/atlassian/server.yaml index 9517ecf3..f0bdd7f2 100644 --- a/servers/atlassian/server.yaml +++ b/servers/atlassian/server.yaml @@ -14,6 +14,7 @@ about: source: project: https://github.com/sooperset/mcp-atlassian branch: v0.11.2 + commit: dd66c3110b68275096176ce843d33ac92fbc74dc config: description: The MCP server is allowed to access these paths secrets: diff --git a/servers/audiense-insights/server.yaml b/servers/audiense-insights/server.yaml index 642d4298..1b253cd4 100644 --- a/servers/audiense-insights/server.yaml +++ b/servers/audiense-insights/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/2454008?s=200&v=4 source: project: https://github.com/AudienseCo/mcp-audiense-insights + commit: f18ffa6ddf944769f3c95944281f752bf6369fa5 config: description: Configure the connection to Audiense Insights secrets: diff --git a/servers/audioscrape/server.yaml b/servers/audioscrape/server.yaml index 43411bd9..dbd0a966 100644 --- a/servers/audioscrape/server.yaml +++ b/servers/audioscrape/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Audioscrape - description: RAG-as-a-Service + description: Search and analyze audio content from a massive database of podcasts, interviews, and other talks icon: https://www.google.com/s2/favicons?domain=audioscrape.com&sz=64 remote: transport_type: streamable-http diff --git a/servers/aws-cdk-mcp-server/server.yaml b/servers/aws-cdk-mcp-server/server.yaml index dd987308..052fa4e8 100644 --- a/servers/aws-cdk-mcp-server/server.yaml +++ b/servers/aws-cdk-mcp-server/server.yaml @@ -12,4 +12,5 @@ about: icon: https://avatars.githubusercontent.com/u/3299148?v=4 source: project: https://github.com/awslabs/mcp + commit: 71b328c395582b1d4ee15e9c02eebc27bef86ca2 directory: src/cdk-mcp-server diff --git a/servers/aws-core-mcp-server/server.yaml b/servers/aws-core-mcp-server/server.yaml index 7ff1031c..428a2d1d 100644 --- a/servers/aws-core-mcp-server/server.yaml +++ b/servers/aws-core-mcp-server/server.yaml @@ -12,4 +12,5 @@ about: icon: https://avatars.githubusercontent.com/u/3299148?v=4 source: project: https://github.com/awslabs/mcp + commit: 71b328c395582b1d4ee15e9c02eebc27bef86ca2 directory: src/core-mcp-server diff --git a/servers/aws-diagram/server.yaml b/servers/aws-diagram/server.yaml index 40c751fd..465e6bdb 100644 --- a/servers/aws-diagram/server.yaml +++ b/servers/aws-diagram/server.yaml @@ -12,4 +12,5 @@ about: icon: https://avatars.githubusercontent.com/u/3299148?v=4 source: project: https://github.com/awslabs/mcp + commit: 71b328c395582b1d4ee15e9c02eebc27bef86ca2 directory: src/aws-diagram-mcp-server diff --git a/servers/aws-documentation/server.yaml b/servers/aws-documentation/server.yaml index 2fdb9a2b..a353ee9d 100644 --- a/servers/aws-documentation/server.yaml +++ b/servers/aws-documentation/server.yaml @@ -12,4 +12,5 @@ about: icon: https://avatars.githubusercontent.com/u/3299148?v=4 source: project: https://github.com/awslabs/mcp + commit: 71b328c395582b1d4ee15e9c02eebc27bef86ca2 directory: src/aws-documentation-mcp-server diff --git a/servers/aws-kb-retrieval-server/server.yaml b/servers/aws-kb-retrieval-server/server.yaml index f5eac7ed..fbca81ff 100644 --- a/servers/aws-kb-retrieval-server/server.yaml +++ b/servers/aws-kb-retrieval-server/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 dockerfile: src/aws-kb-retrieval-server/Dockerfile config: description: Configure the connection to AWS diff --git a/servers/aws-terraform/server.yaml b/servers/aws-terraform/server.yaml index 529ff2ed..cc9c0413 100644 --- a/servers/aws-terraform/server.yaml +++ b/servers/aws-terraform/server.yaml @@ -12,4 +12,5 @@ about: icon: https://avatars.githubusercontent.com/u/3299148?v=4 source: project: https://github.com/awslabs/mcp + commit: 71b328c395582b1d4ee15e9c02eebc27bef86ca2 directory: src/terraform-mcp-server diff --git a/servers/azure/server.yaml b/servers/azure/server.yaml index 8e5c6d41..a418d1a9 100644 --- a/servers/azure/server.yaml +++ b/servers/azure/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/Azure/azure-mcp branch: main + commit: 1ea702cb489ba95c5d9bea8d41fc18e9343703f8 run: command: - server diff --git a/servers/beagle-security/server.yaml b/servers/beagle-security/server.yaml index e958adf7..90196102 100644 --- a/servers/beagle-security/server.yaml +++ b/servers/beagle-security/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/beaglesecurity/beagle-security-mcp-server branch: main + commit: 4b0e0ee2f309d7fa0e19c9e63feee2a4ea2e9f3a config: description: Configure the connection to Beagle security mcp server secrets: diff --git a/servers/bitrefill/server.yaml b/servers/bitrefill/server.yaml index 88114895..ad154962 100644 --- a/servers/bitrefill/server.yaml +++ b/servers/bitrefill/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/bitrefill/bitrefill-mcp-server branch: master + commit: c7e486505ee35756f667193d2cea540d9d53bedb config: description: Configure the connection to Bitrefill secrets: diff --git a/servers/box-remote/readme.md b/servers/box-remote/readme.md deleted file mode 100644 index dd30c155..00000000 --- a/servers/box-remote/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://developer.box.com/guides/box-mcp/remote/ diff --git a/servers/box-remote/server.yaml b/servers/box-remote/server.yaml deleted file mode 100644 index 0e1d8041..00000000 --- a/servers/box-remote/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: box-remote -type: remote -dynamic: - tools: true -meta: - category: storage - tags: - - storage - - document-management - - collaboration - - remote -about: - title: Box - description: Document Management - icon: https://www.google.com/s2/favicons?domain=box.com&sz=64 -remote: - transport_type: streamable-http - url: https://mcp.box.com -oauth: - - provider: box-remote - secret: box-remote.personal_access_token - env: BOX_PERSONAL_ACCESS_TOKEN diff --git a/servers/box-remote/tools.json b/servers/box-remote/tools.json deleted file mode 100644 index 144cbd68..00000000 --- a/servers/box-remote/tools.json +++ /dev/null @@ -1,62 +0,0 @@ -[ - { - "name": "box_who_am_i", - "description": "Get current user information", - "arguments": [] - }, - { - "name": "box_authorize_app_tool", - "description": "Start Box application authorization", - "arguments": [] - }, - { - "name": "box_search_tool", - "description": "Search for files in Box", - "arguments": [] - }, - { - "name": "box_read_tool", - "description": "Read text content of Box files", - "arguments": [] - }, - { - "name": "box_ai_ask_file_single_tool", - "description": "Query AI about a single file", - "arguments": [] - }, - { - "name": "box_ai_ask_file_multi_tool", - "description": "Query AI using multiple files", - "arguments": [] - }, - { - "name": "box_ai_ask_hub_tool", - "description": "Ask AI about a hub", - "arguments": [] - }, - { - "name": "box_ai_extract_freeform_tool", - "description": "Extract data using freeform AI prompts", - "arguments": [] - }, - { - "name": "box_ai_extract_structured_using_fields_tool", - "description": "Extract structured data with specified fields", - "arguments": [] - }, - { - "name": "box_upload_file_from_path_tool", - "description": "Upload files from local filesystem", - "arguments": [] - }, - { - "name": "box_upload_file_from_content_tool", - "description": "Upload content as files", - "arguments": [] - }, - { - "name": "box_download_file_tool", - "description": "Download files from Box", - "arguments": [] - } -] \ No newline at end of file diff --git a/servers/box/server.yaml b/servers/box/server.yaml index ff6d2c05..0b9848d4 100644 --- a/servers/box/server.yaml +++ b/servers/box/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/dgageot/mcp-server-box branch: dockerfile + commit: ec5b6680117610028d4320c9463b66309d95452d config: description: Configure the connection to Box secrets: diff --git a/servers/brave/server.yaml b/servers/brave/server.yaml index 80a2976e..dbfdebba 100644 --- a/servers/brave/server.yaml +++ b/servers/brave/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/12301619?s=200&v=4 source: project: https://github.com/brave/brave-search-mcp-server + commit: 985fb2b8b07ecea0b882a062c1cc336c5cdc7217 config: description: Configure the Brave Search API connection secrets: diff --git a/servers/browserbase/server.yaml b/servers/browserbase/server.yaml new file mode 100644 index 00000000..bc9b546c --- /dev/null +++ b/servers/browserbase/server.yaml @@ -0,0 +1,31 @@ +name: browserbase +image: mcp/browserbase +type: server +meta: + category: devops + tags: + - browserbase + - browser-automation + - web-scraping + - devops +about: + title: Browserbase + description: Allow LLMs to control a browser with Browserbase and Stagehand for AI-powered web automation, intelligent data extraction, and screenshot capture. + icon: https://avatars.githubusercontent.com/u/158221360?s=200&v=4 +source: + project: https://github.com/browserbase/mcp-server-browserbase + commit: b7ec6f6cf0b2224a3ca9952ac158eaa8e600f929 + dockerfile: Dockerfile +config: + description: Configure Browserbase API credentials for browser automation + secrets: + - name: browserbase.browserbase_api_key + env: BROWSERBASE_API_KEY + example: YOUR_BROWSERBASE_API_KEY_HERE + - name: browserbase.browserbase_project_id + env: BROWSERBASE_PROJECT_ID + example: YOUR_BROWSERBASE_PROJECT_ID_HERE + - name: browserbase.gemini_api_key + env: GEMINI_API_KEY + example: YOUR_GEMINI_API_KEY_HERE + optional: true diff --git a/servers/buildkite/server.yaml b/servers/buildkite/server.yaml index 798dd173..5fbafa37 100644 --- a/servers/buildkite/server.yaml +++ b/servers/buildkite/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/5055988?v=4 source: project: https://github.com/buildkite/buildkite-mcp-server + commit: 62670a26a75f26204a6d0ac09bcef91fb246586d dockerfile: Dockerfile.local config: description: Configure the connection to Buildkite diff --git a/servers/camunda/server.yaml b/servers/camunda/server.yaml index 4a98d52a..9b80d3a4 100644 --- a/servers/camunda/server.yaml +++ b/servers/camunda/server.yaml @@ -12,12 +12,13 @@ about: source: project: https://github.com/lepoco/mcp-camunda branch: main + commit: 4b50f37af26a66cced81895ba9b6d71b382ff022 config: description: Configure the connection to Camunda BPM process engine MCP Server env: - name: CAMUNDA_HOST example: http://host.docker.internal:8080/ - value: '{{camunda.camunda_host}}' + value: '{{camunda.camundahost}}' parameters: type: object properties: diff --git a/servers/canva/readme.md b/servers/canva/readme.md deleted file mode 100644 index 93b45d09..00000000 --- a/servers/canva/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://www.canva.com/help/mcp-agent-setup/ diff --git a/servers/canva/server.yaml b/servers/canva/server.yaml deleted file mode 100644 index 4fdc4f4d..00000000 --- a/servers/canva/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: canva -type: remote -dynamic: - tools: true -meta: - category: design - tags: - - design - - graphics - - creative - - remote -about: - title: Canva - description: Design - icon: https://www.google.com/s2/favicons?domain=canva.com&sz=64 -remote: - transport_type: streamable-http - url: https://mcp.canva.com/mcp -oauth: - - provider: canva - secret: canva.personal_access_token - env: CANVA_PERSONAL_ACCESS_TOKEN diff --git a/servers/carbon-voice/server.yaml b/servers/carbon-voice/server.yaml index d1437bdb..12913ce3 100644 --- a/servers/carbon-voice/server.yaml +++ b/servers/carbon-voice/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Carbon Voice - description: Productivity + description: Communicate asynchronously with voice messages featuring AI transcription, summaries, and automated action item extraction icon: https://www.google.com/s2/favicons?domain=carbonvoice.app&sz=64 remote: transport_type: streamable-http diff --git a/servers/cdata-connectcloud/server.yaml b/servers/cdata-connectcloud/server.yaml index 7648be15..0e1a12a3 100644 --- a/servers/cdata-connectcloud/server.yaml +++ b/servers/cdata-connectcloud/server.yaml @@ -11,11 +11,11 @@ about: icon: https://avatars.githubusercontent.com/u/24947354?v=4 source: project: https://github.com/CDataSoftware/connectcloud-mcp-server - branch: 00fe03b372819d143f266fd49e53d98181f4dd12 + commit: 00fe03b372819d143f266fd49e53d98181f4dd12 config: description: Configure the connection to CData Connect Cloud secrets: - - name: cdata.pat + - name: cdata-connectcloud.pat env: CDATA_PAT example: env: @@ -25,7 +25,7 @@ config: parameters: type: object properties: - client_id: + username: type: string required: - username diff --git a/servers/charmhealth-mcp-server/server.yaml b/servers/charmhealth-mcp-server/server.yaml index db11b520..0410530f 100644 --- a/servers/charmhealth-mcp-server/server.yaml +++ b/servers/charmhealth-mcp-server/server.yaml @@ -14,6 +14,7 @@ about: icon: https://raw.githubusercontent.com/CharmHealth/charm-mcp-server/d7928f1ed514f59656e36693749dffea52ab04c2/Charm_icon.png source: project: https://github.com/CharmHealth/charm-mcp-server + commit: 3e519c302e8584bea719a8926ab3a43d7412a104 config: description: Configure the connection to CharmHealth EHR secrets: diff --git a/servers/chroma/server.yaml b/servers/chroma/server.yaml index cf08c1d6..bc654883 100644 --- a/servers/chroma/server.yaml +++ b/servers/chroma/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/105881770?s=200&v=4 source: project: https://github.com/chroma-core/chroma-mcp + commit: 98ff67589bdcc31b730a5415ff9529433f949077 config: secrets: - name: chroma.api_key diff --git a/servers/circleci/server.yaml b/servers/circleci/server.yaml index db7710a1..1bafff28 100644 --- a/servers/circleci/server.yaml +++ b/servers/circleci/server.yaml @@ -13,6 +13,7 @@ source: upstream: https://github.com/CircleCI-Public/mcp-server-circleci project: https://github.com/cmrigney/mcp-server-circleci branch: use-cmd-docker + commit: 34ac141d8ceb27c829932cf5689da2c1c2c21b93 run: command: - stdio diff --git a/servers/clickhouse/server.yaml b/servers/clickhouse/server.yaml index ca77cdb6..2a686457 100644 --- a/servers/clickhouse/server.yaml +++ b/servers/clickhouse/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/54801242?v=4 source: project: https://github.com/ClickHouse/mcp-clickhouse + commit: 415c3b30c5d262cff76b1184e96d7196487c82bf config: description: Configure the connection to ClickHouse secrets: diff --git a/servers/close/server.yaml b/servers/close/server.yaml index 8c9d0c20..16c67967 100644 --- a/servers/close/server.yaml +++ b/servers/close/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Close - description: CRM + description: Streamline sales processes with integrated calling, email, SMS, and automated workflows for small and scaling businesses icon: https://www.google.com/s2/favicons?domain=close.com&sz=64 remote: transport_type: streamable-http diff --git a/servers/cloud-run-mcp/server.yaml b/servers/cloud-run-mcp/server.yaml index 37d19971..02c48c86 100644 --- a/servers/cloud-run-mcp/server.yaml +++ b/servers/cloud-run-mcp/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/2810941?v=4 source: project: https://github.com/GoogleCloudPlatform/cloud-run-mcp + commit: 307962b22a48a06805076c02d462b537f50d3824 config: description: Configure the connection to Google Cloud Run env: diff --git a/servers/cloudflare-ai-gateway/readme.md b/servers/cloudflare-ai-gateway/readme.md new file mode 100644 index 00000000..10d89e90 --- /dev/null +++ b/servers/cloudflare-ai-gateway/readme.md @@ -0,0 +1 @@ +Docs: https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/ai-gateway \ No newline at end of file diff --git a/servers/cloudflare-ai-gateway/server.yaml b/servers/cloudflare-ai-gateway/server.yaml new file mode 100644 index 00000000..b0ce0efe --- /dev/null +++ b/servers/cloudflare-ai-gateway/server.yaml @@ -0,0 +1,23 @@ +name: cloudflare-ai-gateway +type: remote +dynamic: + tools: true +meta: + category: ai + tags: + - ai + - gateway + - logs + - cloudflare + - remote +about: + title: Cloudflare AI Gateway + description: Search your logs, get details about the prompts and responses + icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 +remote: + transport_type: sse + url: https://ai-gateway.mcp.cloudflare.com/sse +oauth: + - provider: cloudflare-ai-gateway + secret: cloudflare-ai-gateway.personal_access_token + env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN \ No newline at end of file diff --git a/servers/cloudflare-ai-gateway/tools.json b/servers/cloudflare-ai-gateway/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/cloudflare-ai-gateway/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/cloudflare-audit-logs/readme.md b/servers/cloudflare-audit-logs/readme.md new file mode 100644 index 00000000..89c146d2 --- /dev/null +++ b/servers/cloudflare-audit-logs/readme.md @@ -0,0 +1 @@ +Docs: https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/auditlogs \ No newline at end of file diff --git a/servers/cloudflare-audit-logs/server.yaml b/servers/cloudflare-audit-logs/server.yaml new file mode 100644 index 00000000..1d27abae --- /dev/null +++ b/servers/cloudflare-audit-logs/server.yaml @@ -0,0 +1,24 @@ +name: cloudflare-audit-logs +type: remote +dynamic: + tools: true +meta: + category: security + tags: + - security + - audit + - logs + - compliance + - cloudflare + - remote +about: + title: Cloudflare Audit Logs + description: Query audit logs and generate reports for review + icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 +remote: + transport_type: sse + url: https://auditlogs.mcp.cloudflare.com/sse +oauth: + - provider: cloudflare-audit-logs + secret: cloudflare-audit-logs.personal_access_token + env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN diff --git a/servers/cloudflare-audit-logs/tools.json b/servers/cloudflare-audit-logs/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/cloudflare-audit-logs/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/cloudflare-autorag/readme.md b/servers/cloudflare-autorag/readme.md new file mode 100644 index 00000000..27a46356 --- /dev/null +++ b/servers/cloudflare-autorag/readme.md @@ -0,0 +1 @@ +Docs: https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/autorag \ No newline at end of file diff --git a/servers/cloudflare-autorag/server.yaml b/servers/cloudflare-autorag/server.yaml new file mode 100644 index 00000000..f9ac1982 --- /dev/null +++ b/servers/cloudflare-autorag/server.yaml @@ -0,0 +1,23 @@ +name: cloudflare-autorag +type: remote +dynamic: + tools: true +meta: + category: ai + tags: + - ai + - rag + - documents + - cloudflare + - remote +about: + title: Cloudflare AutoRAG + description: List and search documents on your AutoRAGs + icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 +remote: + transport_type: sse + url: https://autorag.mcp.cloudflare.com/sse +oauth: + - provider: cloudflare-autorag + secret: cloudflare-autorag.personal_access_token + env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN \ No newline at end of file diff --git a/servers/cloudflare-autorag/tools.json b/servers/cloudflare-autorag/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/cloudflare-autorag/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/cloudflare-browser-rendering/readme.md b/servers/cloudflare-browser-rendering/readme.md new file mode 100644 index 00000000..22585b79 --- /dev/null +++ b/servers/cloudflare-browser-rendering/readme.md @@ -0,0 +1 @@ +Docs: https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/browser-rendering \ No newline at end of file diff --git a/servers/cloudflare-browser-rendering/server.yaml b/servers/cloudflare-browser-rendering/server.yaml new file mode 100644 index 00000000..22efc842 --- /dev/null +++ b/servers/cloudflare-browser-rendering/server.yaml @@ -0,0 +1,24 @@ +name: cloudflare-browser-rendering +type: remote +dynamic: + tools: true +meta: + category: tools + tags: + - tools + - browser + - rendering + - screenshots + - cloudflare + - remote +about: + title: Cloudflare Browser Rendering + description: Fetch web pages, convert them to markdown and take screenshots + icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 +remote: + transport_type: sse + url: https://browser.mcp.cloudflare.com/sse +oauth: + - provider: cloudflare-browser-rendering + secret: cloudflare-browser-rendering.personal_access_token + env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN \ No newline at end of file diff --git a/servers/cloudflare-browser-rendering/tools.json b/servers/cloudflare-browser-rendering/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/cloudflare-browser-rendering/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/cloudflare-container/readme.md b/servers/cloudflare-container/readme.md new file mode 100644 index 00000000..0eb5a3fd --- /dev/null +++ b/servers/cloudflare-container/readme.md @@ -0,0 +1 @@ +Docs: https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/sandbox-container \ No newline at end of file diff --git a/servers/cloudflare-container/server.yaml b/servers/cloudflare-container/server.yaml new file mode 100644 index 00000000..10356a08 --- /dev/null +++ b/servers/cloudflare-container/server.yaml @@ -0,0 +1,23 @@ +name: cloudflare-container +type: remote +dynamic: + tools: true +meta: + category: devops + tags: + - devops + - containers + - development + - cloudflare + - remote +about: + title: Cloudflare Container + description: Spin up a sandbox development environment + icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 +remote: + transport_type: sse + url: https://containers.mcp.cloudflare.com/sse +oauth: + - provider: cloudflare-container + secret: cloudflare-container.personal_access_token + env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN \ No newline at end of file diff --git a/servers/cloudflare-container/tools.json b/servers/cloudflare-container/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/cloudflare-container/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/cloudflare-digital-experience-monitoring/readme.md b/servers/cloudflare-digital-experience-monitoring/readme.md new file mode 100644 index 00000000..534e0cdb --- /dev/null +++ b/servers/cloudflare-digital-experience-monitoring/readme.md @@ -0,0 +1 @@ +Docs: https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/dex-analysis \ No newline at end of file diff --git a/servers/cloudflare-digital-experience-monitoring/server.yaml b/servers/cloudflare-digital-experience-monitoring/server.yaml new file mode 100644 index 00000000..07262597 --- /dev/null +++ b/servers/cloudflare-digital-experience-monitoring/server.yaml @@ -0,0 +1,23 @@ +name: cloudflare-digital-experience-monitoring +type: remote +dynamic: + tools: true +meta: + category: monitoring + tags: + - monitoring + - digital-experience + - applications + - cloudflare + - remote +about: + title: Cloudflare Digital Experience Monitoring + description: Get quick insight on critical applications for your organization + icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 +remote: + transport_type: sse + url: https://dex.mcp.cloudflare.com/sse +oauth: + - provider: cloudflare-digital-experience-monitoring + secret: cloudflare-digital-experience-monitoring.personal_access_token + env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN diff --git a/servers/cloudflare-digital-experience-monitoring/tools.json b/servers/cloudflare-digital-experience-monitoring/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/cloudflare-digital-experience-monitoring/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/cloudflare-dns-analytics/readme.md b/servers/cloudflare-dns-analytics/readme.md new file mode 100644 index 00000000..196fd85f --- /dev/null +++ b/servers/cloudflare-dns-analytics/readme.md @@ -0,0 +1 @@ +Docs: https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/dns-analytics \ No newline at end of file diff --git a/servers/cloudflare-dns-analytics/server.yaml b/servers/cloudflare-dns-analytics/server.yaml new file mode 100644 index 00000000..e3353d8f --- /dev/null +++ b/servers/cloudflare-dns-analytics/server.yaml @@ -0,0 +1,23 @@ +name: cloudflare-dns-analytics +type: remote +dynamic: + tools: true +meta: + category: analytics + tags: + - analytics + - dns + - performance + - cloudflare + - remote +about: + title: Cloudflare DNS Analytics + description: Optimize DNS performance and debug issues based on current set up + icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 +remote: + transport_type: sse + url: https://dns-analytics.mcp.cloudflare.com/sse +oauth: + - provider: cloudflare-dns-analytics + secret: cloudflare-dns-analytics.personal_access_token + env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN \ No newline at end of file diff --git a/servers/cloudflare-dns-analytics/tools.json b/servers/cloudflare-dns-analytics/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/cloudflare-dns-analytics/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/cloudflare-graphql/readme.md b/servers/cloudflare-graphql/readme.md new file mode 100644 index 00000000..9a9a24ff --- /dev/null +++ b/servers/cloudflare-graphql/readme.md @@ -0,0 +1 @@ +Docs: https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/graphql \ No newline at end of file diff --git a/servers/cloudflare-graphql/server.yaml b/servers/cloudflare-graphql/server.yaml new file mode 100644 index 00000000..777c73c6 --- /dev/null +++ b/servers/cloudflare-graphql/server.yaml @@ -0,0 +1,23 @@ +name: cloudflare-graphql +type: remote +dynamic: + tools: true +meta: + category: analytics + tags: + - analytics + - graphql + - api + - cloudflare + - remote +about: + title: Cloudflare GraphQL + description: Get analytics data using Cloudflare's GraphQL API + icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 +remote: + transport_type: sse + url: https://graphql.mcp.cloudflare.com/sse +oauth: + - provider: cloudflare-graphql + secret: cloudflare-graphql.personal_access_token + env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN \ No newline at end of file diff --git a/servers/cloudflare-graphql/tools.json b/servers/cloudflare-graphql/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/cloudflare-graphql/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/cloudflare-logpush/readme.md b/servers/cloudflare-logpush/readme.md new file mode 100644 index 00000000..e41d82e2 --- /dev/null +++ b/servers/cloudflare-logpush/readme.md @@ -0,0 +1 @@ +Docs: https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/logpush \ No newline at end of file diff --git a/servers/cloudflare-logpush/server.yaml b/servers/cloudflare-logpush/server.yaml new file mode 100644 index 00000000..5f5ca0b8 --- /dev/null +++ b/servers/cloudflare-logpush/server.yaml @@ -0,0 +1,23 @@ +name: cloudflare-logpush +type: remote +dynamic: + tools: true +meta: + category: monitoring + tags: + - monitoring + - logs + - logpush + - cloudflare + - remote +about: + title: Cloudflare Logpush + description: Get quick summaries for Logpush job health + icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 +remote: + transport_type: sse + url: https://logs.mcp.cloudflare.com/sse +oauth: + - provider: cloudflare-logpush + secret: cloudflare-logpush.personal_access_token + env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN \ No newline at end of file diff --git a/servers/cloudflare-logpush/tools.json b/servers/cloudflare-logpush/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/cloudflare-logpush/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/cloudflare-observability/server.yaml b/servers/cloudflare-observability/server.yaml index c258cf94..8f7bab95 100644 --- a/servers/cloudflare-observability/server.yaml +++ b/servers/cloudflare-observability/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Cloudflare Observability - description: Observability + description: Monitor application performance, analyze logs, and gain insights with comprehensive observability tools and real-time analytics icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 remote: transport_type: sse diff --git a/servers/cloudflare-one-casb/readme.md b/servers/cloudflare-one-casb/readme.md new file mode 100644 index 00000000..2ab06d72 --- /dev/null +++ b/servers/cloudflare-one-casb/readme.md @@ -0,0 +1 @@ +Docs: https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/cloudflare-one-casb \ No newline at end of file diff --git a/servers/cloudflare-one-casb/server.yaml b/servers/cloudflare-one-casb/server.yaml new file mode 100644 index 00000000..00cf18f0 --- /dev/null +++ b/servers/cloudflare-one-casb/server.yaml @@ -0,0 +1,23 @@ +name: cloudflare-one-casb +type: remote +dynamic: + tools: true +meta: + category: security + tags: + - security + - casb + - saas + - cloudflare-one + - remote +about: + title: Cloudflare One CASB + description: Quickly identify any security misconfigurations for SaaS applications to safeguard users & data + icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 +remote: + transport_type: sse + url: https://casb.mcp.cloudflare.com/sse +oauth: + - provider: cloudflare-one-casb + secret: cloudflare-one-casb.personal_access_token + env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN diff --git a/servers/cloudflare-one-casb/tools.json b/servers/cloudflare-one-casb/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/cloudflare-one-casb/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/cloudflare-radar/readme.md b/servers/cloudflare-radar/readme.md new file mode 100644 index 00000000..832a612c --- /dev/null +++ b/servers/cloudflare-radar/readme.md @@ -0,0 +1 @@ +Docs: https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/radar \ No newline at end of file diff --git a/servers/cloudflare-radar/server.yaml b/servers/cloudflare-radar/server.yaml new file mode 100644 index 00000000..ec6d3eac --- /dev/null +++ b/servers/cloudflare-radar/server.yaml @@ -0,0 +1,22 @@ +name: cloudflare-radar +type: remote +dynamic: + tools: true +meta: + category: analytics + tags: + - analytics + - internet-traffic + - cloudflare + - remote +about: + title: Cloudflare Radar + description: Get global Internet traffic insights, trends, URL scans, and other utilities + icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 +remote: + transport_type: sse + url: https://radar.mcp.cloudflare.com/sse +oauth: + - provider: cloudflare-radar + secret: cloudflare-radar.personal_access_token + env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN \ No newline at end of file diff --git a/servers/cloudflare-radar/tools.json b/servers/cloudflare-radar/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/cloudflare-radar/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/cloudflare-workers/readme.md b/servers/cloudflare-workers-bindings/readme.md similarity index 100% rename from servers/cloudflare-workers/readme.md rename to servers/cloudflare-workers-bindings/readme.md diff --git a/servers/cloudflare-workers/server.yaml b/servers/cloudflare-workers-bindings/server.yaml similarity index 56% rename from servers/cloudflare-workers/server.yaml rename to servers/cloudflare-workers-bindings/server.yaml index f43ecc68..9774116d 100644 --- a/servers/cloudflare-workers/server.yaml +++ b/servers/cloudflare-workers-bindings/server.yaml @@ -1,4 +1,4 @@ -name: cloudflare-workers +name: cloudflare-workers-bindings type: remote dynamic: tools: true @@ -10,13 +10,13 @@ meta: - cloudflare - remote about: - title: Cloudflare Workers - description: Software Development + title: Cloudflare Workers Bindings + description: Build Workers applications with storage, AI, and compute primitives icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 remote: transport_type: sse url: https://bindings.mcp.cloudflare.com/sse oauth: - - provider: cloudflare-workers - secret: cloudflare-workers.personal_access_token + - provider: cloudflare-workers-bindings + secret: cloudflare-workers-bindings.personal_access_token env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN diff --git a/servers/canva/tools.json b/servers/cloudflare-workers-bindings/tools.json similarity index 100% rename from servers/canva/tools.json rename to servers/cloudflare-workers-bindings/tools.json diff --git a/servers/cloudflare-workers-builds/readme.md b/servers/cloudflare-workers-builds/readme.md new file mode 100644 index 00000000..07accb77 --- /dev/null +++ b/servers/cloudflare-workers-builds/readme.md @@ -0,0 +1 @@ +Docs: https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/workers-builds \ No newline at end of file diff --git a/servers/cloudflare-workers-builds/server.yaml b/servers/cloudflare-workers-builds/server.yaml new file mode 100644 index 00000000..5533d5bc --- /dev/null +++ b/servers/cloudflare-workers-builds/server.yaml @@ -0,0 +1,22 @@ +name: cloudflare-workers-builds +type: remote +dynamic: + tools: true +meta: + category: devops + tags: + - devops + - builds + - cloudflare + - remote +about: + title: Cloudflare Workers Builds + description: Get insights and manage your Cloudflare Workers Builds + icon: https://www.google.com/s2/favicons?domain=cloudflare.com&sz=64 +remote: + transport_type: sse + url: https://builds.mcp.cloudflare.com/sse +oauth: + - provider: cloudflare-workers-builds + secret: cloudflare-workers-builds.personal_access_token + env: CLOUDFLARE_PERSONAL_ACCESS_TOKEN \ No newline at end of file diff --git a/servers/cloudflare-workers-builds/tools.json b/servers/cloudflare-workers-builds/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/cloudflare-workers-builds/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/cloudflare-workers/tools.json b/servers/cloudflare-workers/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/cloudflare-workers/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/cloudinary-asset-management/readme.md b/servers/cloudinary-asset-management/readme.md deleted file mode 100644 index 4893cdf6..00000000 --- a/servers/cloudinary-asset-management/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://cloudinary.com/documentation/cloudinary_llm_mcp diff --git a/servers/cloudinary-asset-management/server.yaml b/servers/cloudinary-asset-management/server.yaml deleted file mode 100644 index 04725cd0..00000000 --- a/servers/cloudinary-asset-management/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: cloudinary-asset-management -type: remote -dynamic: - tools: true -meta: - category: media - tags: - - media - - asset-management - - cdn - - remote -about: - title: Cloudinary Asset Management - description: Asset Management - icon: https://www.google.com/s2/favicons?domain=cloudinary.com&sz=64 -remote: - transport_type: sse - url: https://asset-management.mcp.cloudinary.com/sse -oauth: - - provider: cloudinary-asset-management - secret: cloudinary-asset-management.personal_access_token - env: CLOUDINARY_PERSONAL_ACCESS_TOKEN diff --git a/servers/cloudinary-asset-management/tools.json b/servers/cloudinary-asset-management/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/cloudinary-asset-management/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/cockroachdb/server.yaml b/servers/cockroachdb/server.yaml index 925588f0..6db0fb00 100644 --- a/servers/cockroachdb/server.yaml +++ b/servers/cockroachdb/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/6748139?v=4 source: project: https://github.com/amineelkouhen/mcp-cockroachdb + commit: 5c323e49ef52dbf1f3d4714dcd2dffa577d368fe config: description: Configure the connection to CockroachDB secrets: diff --git a/servers/context7/server.yaml b/servers/context7/server.yaml index af56bc52..60c207ec 100644 --- a/servers/context7/server.yaml +++ b/servers/context7/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/upstash/context7 branch: v1.0.14 + commit: f2f367d8913843bd28b2a96a6ce860f43e3fc3ca run: env: MCP_TRANSPORT: stdio diff --git a/servers/couchbase/server.yaml b/servers/couchbase/server.yaml index 4d38bbe0..e5030716 100644 --- a/servers/couchbase/server.yaml +++ b/servers/couchbase/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/605755?s=200&v=4 source: project: https://github.com/Couchbase-Ecosystem/mcp-server-couchbase + commit: 54df571411c47ebfe4241f98f94161aee36a456b config: description: Configure the connection to Couchbase secrets: diff --git a/servers/cylera-mcp-server/server.yaml b/servers/cylera-mcp-server/server.yaml new file mode 100644 index 00000000..3d7c696c --- /dev/null +++ b/servers/cylera-mcp-server/server.yaml @@ -0,0 +1,27 @@ +name: cylera-mcp-server +image: mcp/cylera-mcp-server +type: server +meta: + category: productivity + tags: + - productivity +about: + title: The official MCP Server for Cylera. + description: | + Brings context about device inventory, threats, risks and utilization powered by the Cylera Partner API into an LLM. + icon: https://github.com/Cylera/cylera-mcp-server/blob/main/assets/cylera_logo.png?raw=true +source: + project: https://github.com/Cylera/cylera-mcp-server + commit: d8034893525a78d5e4bfca65dd291e7d498f8f0e +config: + description: Configure the connection to the official MCP Server for Cylera. + secrets: + - name: cylera-mcp-server.cylera.base_url + env: CYLERA_BASE_URL + example: "https://partner.us1.cylera.com Or https://partner.uk1.cylera.com/ Or https://partner.demo.cylera.com" + - name: cylera-mcp-server.cylera.username + env: CYLERA_USERNAME + example: "Your username you use to login to Cylera" + - name: cylera-mcp-server.cylera.password + env: CYLERA_PASSWORD + example: "Your password you use to login to Cylera" diff --git a/servers/cylera-mcp-server/tools.json b/servers/cylera-mcp-server/tools.json new file mode 100644 index 00000000..c2286373 --- /dev/null +++ b/servers/cylera-mcp-server/tools.json @@ -0,0 +1,37 @@ +[ + { + "name": "get_device", + "description": "Get details about a device by MAC address", + "arguments": [] + }, + { + "name": "get_procedures", + "description": "Provide details about how the device has been utilized recently by providing details of the procedures performed", + "arguments": [] + }, + { + "name": "get_device_attributes", + "description": "Get attributes for a device by MAC address", + "arguments": [] + }, + { + "name": "get_risk_mitigations", + "description": "Get risk mitigations for a given CVE reference", + "arguments": [] + }, + { + "name": "get_subnets", + "description": "Get a list of subnets with optional filtering", + "arguments": [] + }, + { + "name": "get_vulnerabilities", + "description": "Returns a paginated list of vulnerabilities with filtering options", + "arguments": [] + }, + { + "name": "search_for_devices", + "description": "Search for devices that match the provided search criteria with comprehensive filtering options", + "arguments": [] + } +] diff --git a/servers/cyreslab-ai-shodan/server.yaml b/servers/cyreslab-ai-shodan/server.yaml index fa842243..cfaa9303 100644 --- a/servers/cyreslab-ai-shodan/server.yaml +++ b/servers/cyreslab-ai-shodan/server.yaml @@ -11,7 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/178556137?s=200&v=4 source: project: https://github.com/Cyreslab-AI/shodan-mcp-server - branch: 935f7044f60570cf2d094d94596eeea7872fb6be + commit: 935f7044f60570cf2d094d94596eeea7872fb6be config: secrets: - name: cyreslab-ai-shodan.api_key diff --git a/servers/dappier/server.yaml b/servers/dappier/server.yaml index 17aa0e8f..5c8a23e8 100644 --- a/servers/dappier/server.yaml +++ b/servers/dappier/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/dappierai/dappier-mcp branch: staging + commit: 38bbde12ef9ea3a7a5fd142d018150c8c53bc63d config: secrets: - name: dappier.api_key diff --git a/servers/dart/server.yaml b/servers/dart/server.yaml index b951bebc..4e72cd01 100644 --- a/servers/dart/server.yaml +++ b/servers/dart/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/92822028?s=200&v=4 source: project: https://github.com/its-dart/dart-mcp-server + commit: 8b2e239afe9f0b6d2cd598383e53dd2c554dc3b7 config: description: Configure the connection to Dart secrets: diff --git a/servers/database-server/readme.md b/servers/database-server/readme.md new file mode 100644 index 00000000..65297916 --- /dev/null +++ b/servers/database-server/readme.md @@ -0,0 +1,132 @@ +# MCP Database Server + +Comprehensive database server supporting PostgreSQL, MySQL, and SQLite with natural language SQL query capabilities. Enables AI agents to interact with databases through both direct SQL and natural language queries. + +## Features + +- **Multi-Database Support**: Works with SQLite, PostgreSQL, and MySQL +- **Natural Language Queries**: Convert natural language to SQL automatically +- **Direct SQL Execution**: Execute raw SQL with safety checks +- **Schema Inspection**: List tables and describe table structures +- **FastAPI Web Interface**: RESTful API endpoints for web integration +- **Docker Ready**: Fully containerized with health checks + +## Available MCP Tools + +### `query_database` +**Description**: Execute natural language queries against the database and convert them to SQL + +**Usage**: Ask questions like "Show me all users created in the last week" or "Find the top 10 products by sales" + +**Arguments**: +- `query` (string): Natural language description of what you want to query from the database + +### `list_tables` +**Description**: List all available tables in the current database + +**Usage**: Ask "What tables are available?" or "Show me the database structure" + +### `describe_table` +**Description**: Get detailed schema information for a specific table including columns, types, and constraints + +**Usage**: Ask "Describe the users table" or "What columns does the products table have?" + +**Arguments**: +- `table_name` (string): Name of the table to describe + +### `execute_sql` +**Description**: Execute raw SQL queries with safety checks and validation + +**Usage**: Execute specific SQL commands when you need precise control + +**Arguments**: +- `query` (string): SQL query to execute + +### `connect_to_database` +**Description**: Connect to a new database using provided connection details + +**Usage**: Switch between different databases during your session + +**Arguments**: +- `connection_string` (string): Database connection string + +### `get_connection_examples` +**Description**: Get example connection strings for different database types (SQLite, PostgreSQL, MySQL) + +**Usage**: Ask "How do I connect to a PostgreSQL database?" or "Show me connection examples" + +### `get_current_database_info` +**Description**: Get information about the currently connected database including type, version, and connection status + +**Usage**: Ask "What database am I connected to?" or "Show me current connection details" + +## Example Queries + +Here are some example questions you can ask your agent: + +```python +# Basic queries +"Show me all users in the database" +"What tables are available?" +"Describe the products table structure" + +# Complex analysis +"Find the top 5 customers by total order value" +"Show me users who registered in the last month" +"What's the average product price by category?" + +# Data exploration +"How many orders were placed yesterday?" +"List all products that are out of stock" +"Find duplicate email addresses in the users table" +``` + +## Configuration + +The server requires a `DATABASE_URL` environment variable with your database connection string: + +### SQLite (recommended for testing) +``` +DATABASE_URL=sqlite+aiosqlite:///data/mydb.db +``` + +### PostgreSQL +``` +DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/mydb +``` + +### MySQL +``` +DATABASE_URL=mysql+aiomysql://user:password@localhost:3306/mydb +``` + +## Docker Usage + +```bash +# Run with SQLite (simplest setup) +docker run -d \ + -p 3000:3000 \ + -e DATABASE_URL=sqlite+aiosqlite:///data/mydb.db \ + souhardyak/mcp-db-server + +# Run with PostgreSQL +docker run -d \ + -p 3000:3000 \ + -e DATABASE_URL=postgresql+asyncpg://user:password@host:5432/db \ + souhardyak/mcp-db-server + +# Run with persistent SQLite storage +docker run -d \ + -p 3000:3000 \ + -v /host/data:/data \ + -e DATABASE_URL=sqlite+aiosqlite:///data/mydb.db \ + souhardyak/mcp-db-server +``` + +## Health Check + +The server provides a health endpoint at `/health` that returns database connectivity status. + +## Source Code + +Full source code and documentation available at: https://github.com/Souhar-dya/mcp-db-server \ No newline at end of file diff --git a/servers/database-server/server.yaml b/servers/database-server/server.yaml new file mode 100644 index 00000000..46b0a1f0 --- /dev/null +++ b/servers/database-server/server.yaml @@ -0,0 +1,35 @@ +name: database-server +image: souhardyak/mcp-db-server +type: server +meta: + category: database + tags: + - database + - sql + - postgresql + - mysql + - sqlite + - natural-language + - ai-assistant +about: + title: MCP Database Server + description: Comprehensive database server supporting PostgreSQL, MySQL, and SQLite with natural language SQL query capabilities. Enables AI agents to interact with databases through both direct SQL and natural language queries. + icon: https://avatars.githubusercontent.com/u/182288589?s=200&v=4 +source: + project: https://github.com/Souhar-dya/mcp-db-server + commit: d47b7ae3bf16e19deb9f2c8853cee255ee9157df +config: + description: Configure the database connection. Supports SQLite, PostgreSQL, and MySQL databases with comprehensive querying capabilities. + env: + - name: DATABASE_URL + example: "sqlite+aiosqlite:///data/mydb.db" + value: '{{database-server.database_url}}' + parameters: + type: object + properties: + database_url: + type: string + title: "Database Connection URL" + description: "Connection string for your database. Examples: SQLite: sqlite+aiosqlite:///data/mydb.db, PostgreSQL: postgresql+asyncpg://user:password@localhost:5432/mydb, MySQL: mysql+aiomysql://user:password@localhost:3306/mydb" + required: + - database_url diff --git a/servers/database-server/tools.json b/servers/database-server/tools.json new file mode 100644 index 00000000..ec29dace --- /dev/null +++ b/servers/database-server/tools.json @@ -0,0 +1,61 @@ +[ + { + "name": "query_database", + "description": "Execute natural language queries against the database and convert them to SQL", + "arguments": [ + { + "name": "query", + "type": "string", + "desc": "Natural language description of what you want to query from the database" + } + ] + }, + { + "name": "list_tables", + "description": "List all available tables in the current database", + "arguments": [] + }, + { + "name": "describe_table", + "description": "Get detailed schema information for a specific table including columns, types, and constraints", + "arguments": [ + { + "name": "table_name", + "type": "string", + "desc": "Name of the table to describe" + } + ] + }, + { + "name": "execute_sql", + "description": "Execute raw SQL queries with safety checks and validation", + "arguments": [ + { + "name": "query", + "type": "string", + "desc": "SQL query to execute" + } + ] + }, + { + "name": "connect_to_database", + "description": "Connect to a new database using provided connection details", + "arguments": [ + { + "name": "connection_string", + "type": "string", + "desc": "Database connection string (e.g., postgresql://user:pass@host:port/db)" + } + ] + }, + { + "name": "get_connection_examples", + "description": "Get example connection strings for different database types (SQLite, PostgreSQL, MySQL)", + "arguments": [] + }, + { + "name": "get_current_database_info", + "description": "Get information about the currently connected database including type, version, and connection status", + "arguments": [] + } +] \ No newline at end of file diff --git a/servers/databutton/server.yaml b/servers/databutton/server.yaml index 669216db..d9c0d454 100644 --- a/servers/databutton/server.yaml +++ b/servers/databutton/server.yaml @@ -11,3 +11,4 @@ about: icon: https://avatars.githubusercontent.com/u/182288589?s=200&v=4 source: project: https://github.com/databutton/databutton-mcp + commit: 3dde0e0dfcb91de131939a622403eeda3c1402b7 diff --git a/servers/descope/server.yaml b/servers/descope/server.yaml index 2b993594..69fc0971 100644 --- a/servers/descope/server.yaml +++ b/servers/descope/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/119703169?s=200&v=4 source: project: https://github.com/descope-sample-apps/descope-mcp-server + commit: e983c18dd340924aa40a1c72ab6608b8b0b35ee7 config: description: Configure the connection to Descope secrets: diff --git a/servers/desktop-commander/server.yaml b/servers/desktop-commander/server.yaml index 017209ae..a39b352e 100644 --- a/servers/desktop-commander/server.yaml +++ b/servers/desktop-commander/server.yaml @@ -21,6 +21,7 @@ about: icon: https://avatars.githubusercontent.com/u/182288589?s=200&v=4 source: project: https://github.com/wonderwhy-er/DesktopCommanderMCP + commit: d40fbcb2fe44b85154ddfba2afb0afd484b42265 run: command: - npm diff --git a/servers/devhub-cms/server.yaml b/servers/devhub-cms/server.yaml index 87f55e03..32a2f70b 100644 --- a/servers/devhub-cms/server.yaml +++ b/servers/devhub-cms/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/602719?s=200&v=4 source: project: https://github.com/devhub/devhub-cms-mcp + commit: c9f904c686a1ceec5d85709d117174d33925558a config: description: Configure the connection to DevHub CMS secrets: diff --git a/servers/dialer/server.yaml b/servers/dialer/server.yaml index 9d215318..e85d0f0c 100644 --- a/servers/dialer/server.yaml +++ b/servers/dialer/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Dialer - description: Outbound Phone Calls + description: Enable AI agents to conduct professional outbound calls for research, interviews, and market validation using your phone number icon: https://www.google.com/s2/favicons?domain=getdialer.app&sz=64 remote: transport_type: sse diff --git a/servers/dockerhub/server.yaml b/servers/dockerhub/server.yaml index 0136aaf3..0f3a1118 100644 --- a/servers/dockerhub/server.yaml +++ b/servers/dockerhub/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/5429470?s=200&v=4 source: project: https://github.com/docker/hub-mcp + commit: 04cfa435b2f0cde66b8c5d4b0d378661400a1ba4 run: command: - --transport=stdio @@ -26,4 +27,4 @@ config: type: object properties: username: - type: string \ No newline at end of file + type: string diff --git a/servers/dreamfactory-mcp/server.yaml b/servers/dreamfactory-mcp/server.yaml index f9d10062..6e5a81ab 100644 --- a/servers/dreamfactory-mcp/server.yaml +++ b/servers/dreamfactory-mcp/server.yaml @@ -12,15 +12,16 @@ about: source: project: https://github.com/dreamfactorysoftware/df-mcp branch: master + commit: ef98884d55917885316ca4ac48002ba16a54a1b8 config: description: Configure the connection to DreamFactory MCP Server env: - name: DREAMFACTORY_URL example: https://your-dreamfactory-instance.com/api/v2/your-api - value: "{{dreamfactory-mcp.dreamfactory_url}}" + value: "{{dreamfactory-mcp.dreamfactoryurl}}" - name: DREAMFACTORY_API_KEY example: 1234abcd1234abdd - value: "{{dreamfactory-mcp.dreamfactory_api_key}}" + value: "{{dreamfactory-mcp.dreamfactoryapikey}}" parameters: type: object properties: diff --git a/servers/duckduckgo/server.yaml b/servers/duckduckgo/server.yaml index 44141f4b..06a48bc0 100644 --- a/servers/duckduckgo/server.yaml +++ b/servers/duckduckgo/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/342708?s=200&v=4 source: project: https://github.com/nickclyde/duckduckgo-mcp-server + commit: d198a2f0e8bd7c862d87d8517e1518aa295f8348 run: allowHosts: - html.duckduckgo.com:443 diff --git a/servers/dynatrace-mcp-server/server.yaml b/servers/dynatrace-mcp-server/server.yaml index f39a4392..4fbe6564 100644 --- a/servers/dynatrace-mcp-server/server.yaml +++ b/servers/dynatrace-mcp-server/server.yaml @@ -14,6 +14,7 @@ about: source: project: https://github.com/dynatrace-oss/dynatrace-mcp branch: main + commit: e1b9fcf382515c51e2c60fc6442b97bebc641b44 config: description: Configure the connection to Dynatrace secrets: diff --git a/servers/e2b/server.yaml b/servers/e2b/server.yaml index 82a93a8a..c0b8574f 100644 --- a/servers/e2b/server.yaml +++ b/servers/e2b/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/129434473?s=200&v=4 source: project: https://github.com/e2b-dev/mcp-server + commit: 40a5cfc21b51b57585ab275bfc01615fb828f799 config: secrets: - name: e2b.api_key diff --git a/servers/edubase/server.yaml b/servers/edubase/server.yaml index e2b2ac7a..5499768f 100644 --- a/servers/edubase/server.yaml +++ b/servers/edubase/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/33938028?s=200&v=4 source: project: https://github.com/EduBase/MCP + commit: fb27455733414f2f98232b6f53c2fb7cb10fe5e1 config: description: Configure the connection to EduBase secrets: diff --git a/servers/effect-mcp/server.yaml b/servers/effect-mcp/server.yaml index 430a0dd8..29662645 100644 --- a/servers/effect-mcp/server.yaml +++ b/servers/effect-mcp/server.yaml @@ -11,3 +11,4 @@ about: icon: https://avatars.githubusercontent.com/u/77678942?v=4 source: project: https://github.com/tim-smart/effect-mcp + commit: 05c19d789d4c4cbd9b881240f815ee8b7fa96ca6 diff --git a/servers/elasticsearch/server.yaml b/servers/elasticsearch/server.yaml index f4882f83..5afbad10 100644 --- a/servers/elasticsearch/server.yaml +++ b/servers/elasticsearch/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/elastic/mcp-server-elasticsearch branch: v0.4.0 + commit: b2912b2ba544060962c7fabf38eb36cc940e9098 run: command: - stdio diff --git a/servers/elevenlabs/server.yaml b/servers/elevenlabs/server.yaml index 8b7e1142..8f70b39f 100644 --- a/servers/elevenlabs/server.yaml +++ b/servers/elevenlabs/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/94471909?v=4 source: project: https://github.com/elevenlabs/elevenlabs-mcp + commit: b0070bde82b742aa1d24822d8a2cab7287c913f8 run: volumes: - '{{elevenlabs.data}}:/root/Desktop' diff --git a/servers/everart/server.yaml b/servers/everart/server.yaml index 32fdb7b0..51ec7f6f 100644 --- a/servers/everart/server.yaml +++ b/servers/everart/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 dockerfile: src/everart/Dockerfile config: secrets: diff --git a/servers/exa/server.yaml b/servers/exa/server.yaml index ec06a6e7..74465aa1 100644 --- a/servers/exa/server.yaml +++ b/servers/exa/server.yaml @@ -12,7 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/77906174?s=200&v=4 source: project: https://github.com/exa-labs/exa-mcp-server - branch: 6ebf32f550168af016837efe31960c6548c831aa + commit: 6ebf32f550168af016837efe31960c6548c831aa config: secrets: - name: exa.api_key diff --git a/servers/explorium/server.yaml b/servers/explorium/server.yaml index 228f1d84..71d839b2 100644 --- a/servers/explorium/server.yaml +++ b/servers/explorium/server.yaml @@ -18,6 +18,7 @@ about: icon: https://marketing-public-links.s3.us-east-1.amazonaws.com/Brand/Logo-dark.png source: project: https://github.com/explorium-ai/mcp-explorium + commit: 68b1d497f6b4b42af581727a4cf08ded3b70a66c config: description: Explorium API Key - Get your API key from https://admin.explorium.ai/api-key secrets: diff --git a/servers/fetch/server.yaml b/servers/fetch/server.yaml index 32fa0a24..cd378064 100644 --- a/servers/fetch/server.yaml +++ b/servers/fetch/server.yaml @@ -13,4 +13,5 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 directory: src/fetch diff --git a/servers/fibery/server.yaml b/servers/fibery/server.yaml index b5418873..d2b077e6 100644 --- a/servers/fibery/server.yaml +++ b/servers/fibery/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/72027989?v=4 source: project: https://github.com/Fibery-inc/fibery-mcp-server + commit: 16b206bd7ee740d2928b72a020660ec4187aba8b config: description: Configure the connection to Fibery secrets: @@ -22,3 +23,8 @@ config: - name: FIBERY_HOST example: your-domain.fibery.io value: '{{fibery.host}}' + parameters: + type: object + properties: + host: + type: string diff --git a/servers/filesystem/server.yaml b/servers/filesystem/server.yaml index 210d9450..114e0213 100644 --- a/servers/filesystem/server.yaml +++ b/servers/filesystem/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 dockerfile: src/filesystem/Dockerfile run: command: diff --git a/servers/firecrawl/server.yaml b/servers/firecrawl/server.yaml index 078dadbe..3d276cdf 100644 --- a/servers/firecrawl/server.yaml +++ b/servers/firecrawl/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/135057108?s=200&v=4 source: project: https://github.com/mendableai/firecrawl-mcp-server + commit: 0b004375cf99c5f5c84e2dced4eba7109feee8bb config: description: Configure the connection to Firecrawl secrets: diff --git a/servers/firefly/server.yaml b/servers/firefly/server.yaml index 16f79745..5a950672 100644 --- a/servers/firefly/server.yaml +++ b/servers/firefly/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Firefly - description: Productivity + description: Transcribe, summarize, and analyze meetings with AI-powered note-taking across video conferencing platforms icon: https://www.google.com/s2/favicons?domain=fireflies.ai&sz=64 remote: transport_type: streamable-http diff --git a/servers/firewalla-mcp-server/server.yaml b/servers/firewalla-mcp-server/server.yaml index c1b874f6..91cbb065 100644 --- a/servers/firewalla-mcp-server/server.yaml +++ b/servers/firewalla-mcp-server/server.yaml @@ -15,6 +15,7 @@ about: icon: https://avatars.githubusercontent.com/u/16805913?s=200&v=4 source: project: https://github.com/amittell/firewalla-mcp-server + commit: d6f8c6ac25701a7eb69c3dfdda312817979329e9 config: description: Configure your Firewalla MSP connection credentials secrets: diff --git a/servers/flexprice/server.yaml b/servers/flexprice/server.yaml index 7aa068dc..d7a8f511 100644 --- a/servers/flexprice/server.yaml +++ b/servers/flexprice/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/168305045?v=4 source: project: https://github.com/flexprice/mcp-server + commit: 87482b1f6c185589d41257ef846dcea0c3f0669b config: description: Configure the connection to Redis secrets: diff --git a/servers/git/server.yaml b/servers/git/server.yaml index 969f3026..62b0c79c 100644 --- a/servers/git/server.yaml +++ b/servers/git/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 directory: src/git run: volumes: diff --git a/servers/github-chat/server.yaml b/servers/github-chat/server.yaml index cf492e11..f7099f34 100644 --- a/servers/github-chat/server.yaml +++ b/servers/github-chat/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/194295535?s=200&v=4 source: project: https://github.com/AsyncFuncAI/github-chat-mcp + commit: 8c1a390f10ab295ea7ce61c32461bbee06d2c654 config: secrets: - name: github-chat.api_key diff --git a/servers/github-official/server.yaml b/servers/github-official/server.yaml index 227bd021..d1b222d1 100644 --- a/servers/github-official/server.yaml +++ b/servers/github-official/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/9919?s=200&v=4 source: project: https://github.com/github/github-mcp-server + commit: 23fa0dd1a821d1346c1de2abafe7327d26981606 run: allowHosts: - api.github.com:443 diff --git a/servers/github-remote/readme.md b/servers/github-remote/readme.md deleted file mode 100644 index d90cacc8..00000000 --- a/servers/github-remote/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://github.com/github/github-mcp-server diff --git a/servers/github-remote/server.yaml b/servers/github-remote/server.yaml deleted file mode 100644 index f075ca07..00000000 --- a/servers/github-remote/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: github-remote -type: remote -dynamic: - tools: true -meta: - category: devops - tags: - - devops - - version-control - - collaboration - - remote -about: - title: GitHub - description: Software Development - icon: https://www.google.com/s2/favicons?domain=githubcopilot.com&sz=64 -remote: - transport_type: streamable-http - url: https://api.githubcopilot.com/mcp -oauth: - - provider: github-remote - secret: github-remote.personal_access_token - env: GITHUB_PERSONAL_ACCESS_TOKEN diff --git a/servers/github-remote/tools.json b/servers/github-remote/tools.json deleted file mode 100644 index ba9a99ad..00000000 --- a/servers/github-remote/tools.json +++ /dev/null @@ -1,107 +0,0 @@ -[ - { - "name": "cancel_workflow_run", - "description": "Cancel a GitHub Actions workflow run", - "arguments": [] - }, - { - "name": "delete_workflow_run_logs", - "description": "Delete logs from a workflow run", - "arguments": [] - }, - { - "name": "download_workflow_run_artifact", - "description": "Download artifacts from a workflow run", - "arguments": [] - }, - { - "name": "get_job_logs", - "description": "Get logs for a specific job", - "arguments": [] - }, - { - "name": "get_workflow_run", - "description": "Get details of a workflow run", - "arguments": [] - }, - { - "name": "list_workflow_jobs", - "description": "List jobs in a workflow", - "arguments": [] - }, - { - "name": "run_workflow", - "description": "Trigger a workflow run", - "arguments": [] - }, - { - "name": "rerun_workflow_run", - "description": "Re-run a workflow", - "arguments": [] - }, - { - "name": "get_code_scanning_alert", - "description": "Get details of a code scanning alert", - "arguments": [] - }, - { - "name": "list_code_scanning_alerts", - "description": "List code scanning alerts", - "arguments": [] - }, - { - "name": "get_dependabot_alert", - "description": "Get details of a Dependabot alert", - "arguments": [] - }, - { - "name": "list_dependabot_alerts", - "description": "List Dependabot alerts", - "arguments": [] - }, - { - "name": "get_discussion", - "description": "Get a GitHub discussion", - "arguments": [] - }, - { - "name": "get_discussion_comments", - "description": "Get comments on a discussion", - "arguments": [] - }, - { - "name": "list_discussions", - "description": "List GitHub discussions", - "arguments": [] - }, - { - "name": "create_gist", - "description": "Create a new GitHub gist", - "arguments": [] - }, - { - "name": "list_gists", - "description": "List GitHub gists", - "arguments": [] - }, - { - "name": "update_gist", - "description": "Update an existing gist", - "arguments": [] - }, - { - "name": "add_issue_comment", - "description": "Add a comment to an issue", - "arguments": [] - }, - { - "name": "create_issue", - "description": "Create a new GitHub issue", - "arguments": [] - }, - { - "name": "assign_copilot_to_issue", - "description": "Assign GitHub Copilot to an issue", - "arguments": [] - } -] \ No newline at end of file diff --git a/servers/github/server.yaml b/servers/github/server.yaml index 89c24125..7a988fe0 100644 --- a/servers/github/server.yaml +++ b/servers/github/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 dockerfile: src/github/Dockerfile config: secrets: diff --git a/servers/gitlab/server.yaml b/servers/gitlab/server.yaml index c3d9aec0..4dc184a9 100644 --- a/servers/gitlab/server.yaml +++ b/servers/gitlab/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 dockerfile: src/gitlab/Dockerfile config: description: Configure the local or Cloud GitLab instance diff --git a/servers/glif/server.yaml b/servers/glif/server.yaml index ed7d7353..2e1c73e9 100644 --- a/servers/glif/server.yaml +++ b/servers/glif/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/119348676?s=200&v=4 source: project: https://github.com/glifxyz/glif-mcp-server + commit: 06af0b4e37aa257591dee5e62b023b96fa8b6f42 config: description: Configure the connection to glif.app secrets: diff --git a/servers/globalping/server.yaml b/servers/globalping/server.yaml index 826d4c16..76064cc9 100644 --- a/servers/globalping/server.yaml +++ b/servers/globalping/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Globalping - description: Software Development + description: Run network tests like ping, traceroute, and DNS queries from hundreds of global probes for monitoring and debugging icon: https://www.google.com/s2/favicons?domain=globalping.io&sz=64 remote: transport_type: sse diff --git a/servers/gmail-mcp/server.yaml b/servers/gmail-mcp/server.yaml index 76caf005..0e859a26 100644 --- a/servers/gmail-mcp/server.yaml +++ b/servers/gmail-mcp/server.yaml @@ -15,6 +15,7 @@ about: icon: https://www.google.com/gmail/about/static/images/logo-gmail.png source: project: https://github.com/Sallytion/Gmail-MCP + commit: c9a31741c7e879575c9dfc90192a7a430efef812 config: description: Configure Gmail access with app password authentication secrets: @@ -45,4 +46,4 @@ config: description: Your Gmail email address format: email required: - - email_address \ No newline at end of file + - email_address diff --git a/servers/google-maps-comprehensive/server.yaml b/servers/google-maps-comprehensive/server.yaml new file mode 100644 index 00000000..b4ec92a7 --- /dev/null +++ b/servers/google-maps-comprehensive/server.yaml @@ -0,0 +1,30 @@ +name: google-maps-comprehensive +image: mcp/google-maps-comprehensive +type: server +meta: + category: devops + tags: + - google-maps + - maps + - geocoding + - directions + - places + - elevation + - location + - devops +about: + title: Google Maps Comprehensive MCP + description: Complete Google Maps integration with 8 tools including geocoding, places search, directions, elevation data, and more using Google's latest APIs. + icon: https://avatars.githubusercontent.com/u/182288589?s=200&v=4 +source: + project: https://github.com/vicpeacock/google-maps-comprehensive-mcp + branch: main + commit: 32d7f4c1f497f97f2cf9f4a74ed5f6ff6daca687 + dockerfile: Dockerfile +config: + description: Configure Google Maps API access with comprehensive location services + secrets: + - name: google-maps-comprehensive.api_key + env: GOOGLE_MAPS_API_KEY + example: + description: Google Maps API key with Places API, Routes API, Geocoding API, and Elevation API enabled diff --git a/servers/google-maps/server.yaml b/servers/google-maps/server.yaml index a74a2fa7..e9b35125 100644 --- a/servers/google-maps/server.yaml +++ b/servers/google-maps/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.6 + commit: 82064568802e542c3924560aef2cb421b4ce436c dockerfile: src/google-maps/Dockerfile config: secrets: diff --git a/servers/grafana/server.yaml b/servers/grafana/server.yaml index 652beaee..59eea8b6 100644 --- a/servers/grafana/server.yaml +++ b/servers/grafana/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/grafana/mcp-grafana branch: main + commit: cd62726746f46263faa904d598137108b0ca27d1 run: command: - --transport=stdio diff --git a/servers/grafbase/server.yaml b/servers/grafbase/server.yaml index 78fa5ef3..053290b7 100644 --- a/servers/grafbase/server.yaml +++ b/servers/grafbase/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Grafbase - description: Software Development + description: Build and deploy high-performance GraphQL APIs with federation, edge computing, and enterprise-grade governance icon: https://www.google.com/s2/favicons?domain=grafbase.com&sz=64 remote: transport_type: streamable-http diff --git a/servers/gyazo/server.yaml b/servers/gyazo/server.yaml index fd50ba98..45efbb0e 100644 --- a/servers/gyazo/server.yaml +++ b/servers/gyazo/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/113151?v=4 source: project: https://github.com/nota/gyazo-mcp-server + commit: fa7285c214c0d11fc3d1cdc71843e0f1eecd4ed1 config: secrets: - name: gyazo.access_token diff --git a/servers/hackle/server.yaml b/servers/hackle/server.yaml index 879bfde9..0615c7dd 100644 --- a/servers/hackle/server.yaml +++ b/servers/hackle/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/65485327?v=4 source: project: https://github.com/hackle-io/hackle-mcp + commit: b355ad09a50067cfa2f6a2d36fab59ae4eeefd08 config: secrets: - name: hackle.api_key diff --git a/servers/handwriting-ocr/server.yaml b/servers/handwriting-ocr/server.yaml index d15dd654..c516de02 100644 --- a/servers/handwriting-ocr/server.yaml +++ b/servers/handwriting-ocr/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/202432073?s=200&v=4 source: project: https://github.com/Handwriting-OCR/handwriting-ocr-mcp-server + commit: 1b81cd90103265eac82d4a441fe669e6c7ccf03a config: secrets: - name: handwriting-ocr.api_token diff --git a/servers/hdx/server.yaml b/servers/hdx/server.yaml index e22621d3..26f4c882 100644 --- a/servers/hdx/server.yaml +++ b/servers/hdx/server.yaml @@ -11,6 +11,7 @@ about: icon: https://raw.githubusercontent.com/dividor/hdx-mcp/main/assets/hdx_mcp_icon_transparent_512.png source: project: https://github.com/dividor/hdx-mcp + commit: c625bf9ae120ed93a36aa7ac9352bdc41f6d9546 config: description: Configure the connection to Humanitarian Data Exchange MCP Server secrets: diff --git a/servers/heroku/server.yaml b/servers/heroku/server.yaml index 914c77aa..8eeb826f 100644 --- a/servers/heroku/server.yaml +++ b/servers/heroku/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/heroku/heroku-mcp-server branch: refs/pull/24/merge + commit: f4949381035a04cb862b323ed024762bd2bb12aa config: secrets: - name: heroku.api_key diff --git a/servers/hostinger-mcp-server/server.yaml b/servers/hostinger-mcp-server/server.yaml index 10a642a2..8e7526ba 100644 --- a/servers/hostinger-mcp-server/server.yaml +++ b/servers/hostinger-mcp-server/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/2630767?v=4 source: project: https://github.com/hostinger/api-mcp-server + commit: 5cf6a59b5f165a35458b8c064de70e9554ca9e65 config: description: Configure the connection to Hostinger API MCP Server secrets: diff --git a/servers/hoverfly-mcp-server/server.yaml b/servers/hoverfly-mcp-server/server.yaml index 3be20fc4..e7de9e4f 100644 --- a/servers/hoverfly-mcp-server/server.yaml +++ b/servers/hoverfly-mcp-server/server.yaml @@ -14,6 +14,7 @@ about: description: A Model Context Protocol (MCP) server that exposes Hoverfly as a programmable tool for AI assistants like Cursor, Claude, GitHub Copilot, and others supporting MCP. It enables dynamic mocking of third-party APIs to unblock development, automate testing, and simulate unavailable services during integration. source: project: https://github.com/kapishmalik/hoverfly-mcp-server + commit: 34e9607ed71f1b78d1fd6788e32332912bac709a run: volumes: - '{{hoverfly-mcp-server.data}}:/opt/hoverfly-mcp/simulation-data' diff --git a/servers/hubspot/server.yaml b/servers/hubspot/server.yaml index 3fd263a7..3f21703d 100644 --- a/servers/hubspot/server.yaml +++ b/servers/hubspot/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: HubSpot - description: CRM + description: Unite marketing, sales, and customer service with AI-powered automation, lead management, and comprehensive analytics icon: https://www.google.com/s2/favicons?domain=hubspot.com&sz=64 remote: transport_type: streamable-http diff --git a/servers/hummingbot-mcp/server.yaml b/servers/hummingbot-mcp/server.yaml index 8a942629..5d0e8c67 100644 --- a/servers/hummingbot-mcp/server.yaml +++ b/servers/hummingbot-mcp/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/88165212?v=4 source: project: https://github.com/hummingbot/mcp + commit: ad24846aa3d62706265fdb28833e721debdb0df0 config: description: 'Configure the MCP connection to the Hummingbot API' secrets: diff --git a/servers/husqvarna-automower/server.yaml b/servers/husqvarna-automower/server.yaml index f6ab10d0..ad0fa2d7 100644 --- a/servers/husqvarna-automower/server.yaml +++ b/servers/husqvarna-automower/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/17142631?v=4 source: project: https://github.com/jeanlaurent/mcp-husqvarna-automower + commit: 288d2c3a10e650308ec03863258c3a72ae08578b config: description: Configure the connection to Husqvarna Automower secrets: diff --git a/servers/hyperbrowser/server.yaml b/servers/hyperbrowser/server.yaml index e32b2f96..5f93e758 100644 --- a/servers/hyperbrowser/server.yaml +++ b/servers/hyperbrowser/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/189776885?s=200&v=4 source: project: https://github.com/hyperbrowserai/mcp + commit: 7492e83cf1825b3cf9725bfa25c5086df702599a config: description: Configure the connection to Hyperbrowser secrets: diff --git a/servers/hyperspell/server.yaml b/servers/hyperspell/server.yaml index 1f632faa..6fe4f954 100644 --- a/servers/hyperspell/server.yaml +++ b/servers/hyperspell/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/170754924?s=200&v=4 source: project: https://github.com/hyperspell/hyperspell-mcp + commit: 6fbf999ec35b02ce4685de9b6309600bcd61c2db config: description: Configure the connection to Hyperspell secrets: diff --git a/servers/iaptic/server.yaml b/servers/iaptic/server.yaml index 1410f221..7859735a 100644 --- a/servers/iaptic/server.yaml +++ b/servers/iaptic/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/72789805?s=200&v=4 source: project: https://github.com/iaptic/mcp-server-iaptic + commit: 210c73ccb12bb61676dbb23302f16a2d408c4f05 config: description: Configure the connection to iaptic secrets: diff --git a/servers/inspektor-gadget/server.yaml b/servers/inspektor-gadget/server.yaml index 9fa0e829..9c0c6813 100644 --- a/servers/inspektor-gadget/server.yaml +++ b/servers/inspektor-gadget/server.yaml @@ -15,12 +15,14 @@ about: icon: https://avatars.githubusercontent.com/u/111520143?s=200&v=4 source: project: https://github.com/inspektor-gadget/ig-mcp-server + commit: 1f452f8ce2456594bac7fcc99ce5f907a037ad19 run: command: - -gadget-discoverer=artifacthub - -gadget-images={{inspektor-gadget.gadget-images}} volumes: - '{{inspektor-gadget.kubeconfig}}:/kubeconfig' + - ig-mcp-cache:/root/.cache/ig-mcp-server config: description: Configuration for the Inspektor Gadget MCP Server parameters: diff --git a/servers/instant/server.yaml b/servers/instant/server.yaml index 4bdfcedf..de76ac3a 100644 --- a/servers/instant/server.yaml +++ b/servers/instant/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Instant - description: Software Development + description: Build real-time, offline-first applications with a modern Firebase-like database and instant synchronization icon: https://www.google.com/s2/favicons?domain=instantdb.com&sz=64 remote: transport_type: streamable-http diff --git a/servers/intercom/readme.md b/servers/intercom/readme.md deleted file mode 100644 index 848beb4c..00000000 --- a/servers/intercom/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://www.intercom.com/blog/introducing-model-context-protocol-fin/ diff --git a/servers/intercom/server.yaml b/servers/intercom/server.yaml deleted file mode 100644 index a95e5a44..00000000 --- a/servers/intercom/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: intercom -type: remote -dynamic: - tools: true -meta: - category: support - tags: - - support - - customer-service - - messaging - - remote -about: - title: Intercom - description: Customer Support - icon: https://www.google.com/s2/favicons?domain=intercom.com&sz=64 -remote: - transport_type: sse - url: https://mcp.intercom.com/sse -oauth: - - provider: intercom - secret: intercom.personal_access_token - env: INTERCOM_PERSONAL_ACCESS_TOKEN diff --git a/servers/intercom/tools.json b/servers/intercom/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/intercom/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/invideo/server.yaml b/servers/invideo/server.yaml index 0dfc18da..e85d5862 100644 --- a/servers/invideo/server.yaml +++ b/servers/invideo/server.yaml @@ -11,12 +11,8 @@ meta: - remote about: title: InVideo - description: Video Platform + description: Create professional marketing videos with AI-powered generation, templates, and automated voiceovers icon: https://www.google.com/s2/favicons?domain=invideo.io&sz=64 remote: transport_type: sse url: https://mcp.invideo.io/sse -oauth: - - provider: invideo - secret: invideo.personal_access_token - env: INVVIDEO_PERSONAL_ACCESS_TOKEN diff --git a/servers/jam/readme.md b/servers/jam/readme.md deleted file mode 100644 index ca4037fe..00000000 --- a/servers/jam/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://jam.dev/docs/debug-a-jam/mcp diff --git a/servers/jam/server.yaml b/servers/jam/server.yaml deleted file mode 100644 index f55048f2..00000000 --- a/servers/jam/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: jam -type: remote -dynamic: - tools: true -meta: - category: devops - tags: - - devops - - debugging - - collaboration - - remote -about: - title: Jam - description: Software Development - icon: https://www.google.com/s2/favicons?domain=jam.dev&sz=64 -remote: - transport_type: streamable-http - url: https://mcp.jam.dev/mcp -oauth: - - provider: jam - secret: jam.personal_access_token - env: JAM_PERSONAL_ACCESS_TOKEN diff --git a/servers/jam/tools.json b/servers/jam/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/jam/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/jetbrains/server.yaml b/servers/jetbrains/server.yaml index 7b9e9b31..ac068943 100644 --- a/servers/jetbrains/server.yaml +++ b/servers/jetbrains/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/878437?s=200&v=4 source: project: https://github.com/GannaChernyshova/mcp-jetbrains + commit: cd2dcb76144c92b8e7b8d32ae354a5bcb672a07f config: description: Configure the connection to the IDE env: diff --git a/servers/kafka-schema-reg-mcp/server.yaml b/servers/kafka-schema-reg-mcp/server.yaml index dc1e7870..faa33877 100644 --- a/servers/kafka-schema-reg-mcp/server.yaml +++ b/servers/kafka-schema-reg-mcp/server.yaml @@ -15,6 +15,7 @@ about: icon: https://avatars.githubusercontent.com/u/47359?s=200&v=4 source: project: https://github.com/aywengo/kafka-schema-reg-mcp + commit: e145fb8dd68d2246f50395572f6ca5c9f100e53c run: allowHosts: - "*:8081" diff --git a/servers/kagisearch/server.yaml b/servers/kagisearch/server.yaml index 24b434e9..6f2227d2 100644 --- a/servers/kagisearch/server.yaml +++ b/servers/kagisearch/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/92134518?s=200&v=4 source: project: https://github.com/kagisearch/kagimcp + commit: f2c2157cd520f37c2a926c6434bc03a29a9f5ceb config: description: Configure the connection to Kagi search secrets: diff --git a/servers/keboola-mcp/server.yaml b/servers/keboola-mcp/server.yaml index 00bd42c5..f60aa5b8 100644 --- a/servers/keboola-mcp/server.yaml +++ b/servers/keboola-mcp/server.yaml @@ -13,6 +13,7 @@ about: icon: https://avatars.githubusercontent.com/u/1424387?v=4 source: project: https://github.com/keboola/mcp-server + commit: 6721e98d208db097f2dba6e7963ff319384c2ea6 config: description: Configure the connection to your Keboola project secrets: diff --git a/servers/kgrag-mcp-server/server.yaml b/servers/kgrag-mcp-server/server.yaml index 77430f1f..9bd1fde6 100644 --- a/servers/kgrag-mcp-server/server.yaml +++ b/servers/kgrag-mcp-server/server.yaml @@ -17,6 +17,7 @@ about: source: project: https://github.com/gzileni/kgrag_mcp_server branch: main + commit: 79ae98a75fb5248a6b574b00d3020b3412e53667 config: description: Configure the connection to the Knowledge Graph MCP Server diff --git a/servers/kollektiv/readme.md b/servers/kollektiv/readme.md deleted file mode 100644 index a980edae..00000000 --- a/servers/kollektiv/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://github.com/alexander-zuev/kollektiv-mcp diff --git a/servers/kollektiv/server.yaml b/servers/kollektiv/server.yaml deleted file mode 100644 index c3f8491e..00000000 --- a/servers/kollektiv/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: kollektiv -type: remote -dynamic: - tools: true -meta: - category: documentation - tags: - - documentation - - knowledge-base - - ai - - remote -about: - title: Kollektiv - description: Documentation - icon: https://www.google.com/s2/favicons?domain=thekollektiv.ai&sz=64 -remote: - transport_type: sse - url: https://mcp.thekollektiv.ai/sse -oauth: - - provider: kollektiv - secret: kollektiv.personal_access_token - env: KOLLEKTIV_PERSONAL_ACCESS_TOKEN diff --git a/servers/kollektiv/tools.json b/servers/kollektiv/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/kollektiv/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/kong/server.yaml b/servers/kong/server.yaml index 73c99a8a..192d1104 100644 --- a/servers/kong/server.yaml +++ b/servers/kong/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/Kong/mcp-konnect branch: refs/pull/7/merge + commit: 1710e9d575c58b0426085756f3796fd46e9db076 config: description: Configure the connection to Kong Konnect secrets: diff --git a/servers/kubectl-mcp-server/server.yaml b/servers/kubectl-mcp-server/server.yaml index 5c710cbf..01b54038 100644 --- a/servers/kubectl-mcp-server/server.yaml +++ b/servers/kubectl-mcp-server/server.yaml @@ -14,6 +14,7 @@ about: icon: https://avatars.githubusercontent.com/u/13629408?s=200&v=4 source: project: https://github.com/rohitg00/kubectl-mcp-server + commit: f0986f3f3817283cf945b00ffb1329a3beef5f0e run: volumes: - '{{kubectl-mcp-server.kubeconfig}}:/root/.kube' diff --git a/servers/kubernetes/server.yaml b/servers/kubernetes/server.yaml index 5fd79021..8a147f06 100644 --- a/servers/kubernetes/server.yaml +++ b/servers/kubernetes/server.yaml @@ -12,3 +12,17 @@ about: icon: https://avatars.githubusercontent.com/u/13629408?s=200&v=4 source: project: https://github.com/Flux159/mcp-server-kubernetes + commit: fd2fef03fd98dbad79064fa783f745f6a18e2041 +run: + volumes: + - '{{kubernetes.config_path}}:/home/appuser/.kube/config' +config: + description: Configure the location of the host Kubernetes file + parameters: + type: object + properties: + config_path: + description: the path to the host .kube/config + type: string + required: + - config_path diff --git a/servers/lara/server.yaml b/servers/lara/server.yaml index 1aad51fe..3a95ff51 100644 --- a/servers/lara/server.yaml +++ b/servers/lara/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/10272138?s=200&v=4 source: project: https://github.com/translated/lara-mcp + commit: 7a664be202ce1c29265b027305dbad0e2d9a8575 config: description: Configure the connection to Lara Translate secrets: diff --git a/servers/line/server.yaml b/servers/line/server.yaml index 0d2b79f6..552ac885 100644 --- a/servers/line/server.yaml +++ b/servers/line/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/13128444?v=4 source: project: https://github.com/line/line-bot-mcp-server + commit: 190adabe83bd96ea04b6653b5bedf818db8eebb2 config: description: Configure the connection to LINE secrets: diff --git a/servers/linear/server.yaml b/servers/linear/server.yaml index 8b70cbb5..fef3e53e 100644 --- a/servers/linear/server.yaml +++ b/servers/linear/server.yaml @@ -11,11 +11,11 @@ meta: - remote about: title: Linear - description: Project Management + description: Track issues, plan sprints, and manage product development with a fast, keyboard-first interface designed for software teams icon: https://www.google.com/s2/favicons?domain=linear.app&sz=64 remote: - transport_type: sse - url: https://mcp.linear.app/sse + transport_type: streamable-http + url: https://mcp.linear.app/mcp oauth: - provider: linear secret: linear.personal_access_token diff --git a/servers/linkedin-mcp-server/server.yaml b/servers/linkedin-mcp-server/server.yaml index 666e0125..dbab945f 100644 --- a/servers/linkedin-mcp-server/server.yaml +++ b/servers/linkedin-mcp-server/server.yaml @@ -16,6 +16,7 @@ about: icon: https://raw.githubusercontent.com/stickerdaniel/linkedin-mcp-server/main/assets/icons/linkedin.svg source: project: https://github.com/stickerdaniel/linkedin-mcp-server + commit: c54e3fa518d2b5cd827262159771b9d85a6eae13 config: description: Configure LinkedIn authentication and optional user agent secrets: @@ -31,4 +32,4 @@ config: properties: user_agent: type: string - description: Custom user agent string (optional, helps avoid detection and cookie login issues) \ No newline at end of file + description: Custom user agent string (optional, helps avoid detection and cookie login issues) diff --git a/servers/listenetic/readme.md b/servers/listenetic/readme.md deleted file mode 100644 index e35c27a9..00000000 --- a/servers/listenetic/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://app.listenetic.com diff --git a/servers/listenetic/server.yaml b/servers/listenetic/server.yaml deleted file mode 100644 index cd2ef19e..00000000 --- a/servers/listenetic/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: listenetic -type: remote -dynamic: - tools: true -meta: - category: productivity - tags: - - productivity - - audio - - transcription - - remote -about: - title: Listenetic - description: Productivity - icon: https://www.google.com/s2/favicons?domain=listenetic.ai&sz=64 -remote: - transport_type: streamable-http - url: https://mcp.listenetic.com/v1/mcp -oauth: - - provider: listenetic - secret: listenetic.personal_access_token - env: LISTENETIC_PERSONAL_ACCESS_TOKEN diff --git a/servers/listenetic/tools.json b/servers/listenetic/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/listenetic/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/maestro-mcp-server/server.yaml b/servers/maestro-mcp-server/server.yaml index 1eda3895..c9c45046 100644 --- a/servers/maestro-mcp-server/server.yaml +++ b/servers/maestro-mcp-server/server.yaml @@ -15,6 +15,7 @@ about: source: project: https://github.com/maestro-org/maestro-mcp-server branch: main + commit: df54020d17b0b6c3b2a03c7a328c644b9f49d970 config: description: Configure the connection to Maestro MCP Server secrets: diff --git a/servers/mapbox-devkit/server.yaml b/servers/mapbox-devkit/server.yaml index 93bf712b..7171a702 100644 --- a/servers/mapbox-devkit/server.yaml +++ b/servers/mapbox-devkit/server.yaml @@ -15,9 +15,10 @@ about: icon: https://avatars.githubusercontent.com/u/600935?s=200&v=4 source: project: https://github.com/mapbox/mcp-devkit-server + commit: 51c2210325bbf687c7e0b4b5f4af5123b0005745 dockerfile: Dockerfile config: secrets: - name: mapbox-devkit.access_token env: MAPBOX_ACCESS_TOKEN - example: YOUR_API_KEY_HERE \ No newline at end of file + example: YOUR_API_KEY_HERE diff --git a/servers/mapbox/server.yaml b/servers/mapbox/server.yaml index ec9be582..c712fe8a 100644 --- a/servers/mapbox/server.yaml +++ b/servers/mapbox/server.yaml @@ -15,9 +15,10 @@ about: icon: https://avatars.githubusercontent.com/u/600935?s=200&v=4 source: project: https://github.com/mapbox/mcp-server + commit: d0ef018ba90168e0e2839ea49fd81d0b83244eb9 dockerfile: Dockerfile config: secrets: - name: mapbox.access_token env: MAPBOX_ACCESS_TOKEN - example: YOUR_API_KEY_HERE \ No newline at end of file + example: YOUR_API_KEY_HERE diff --git a/servers/markdownify/server.yaml b/servers/markdownify/server.yaml index 8877fd8d..aeff9fe5 100644 --- a/servers/markdownify/server.yaml +++ b/servers/markdownify/server.yaml @@ -20,6 +20,7 @@ about: icon: https://avatars.githubusercontent.com/u/182288589?s=200&v=4 source: project: https://github.com/zcaceres/markdownify-mcp + commit: 4add350f4643365d31f7b3385ab1b65d37f97f1e run: volumes: - "{{markdownify.paths|volume|into}}" diff --git a/servers/markitdown/server.yaml b/servers/markitdown/server.yaml index 75720bb6..c9d09eed 100644 --- a/servers/markitdown/server.yaml +++ b/servers/markitdown/server.yaml @@ -20,6 +20,7 @@ about: icon: https://avatars.githubusercontent.com/u/6154722?v=4 source: project: https://github.com/microsoft/markitdown + commit: 8a9d8f15936b2068bcb39ccc8d3b317f93784d86 directory: packages/markitdown-mcp run: volumes: diff --git a/servers/maven-tools-mcp/server.yaml b/servers/maven-tools-mcp/server.yaml index 0db31cd0..084b5020 100644 --- a/servers/maven-tools-mcp/server.yaml +++ b/servers/maven-tools-mcp/server.yaml @@ -18,4 +18,5 @@ about: description: JVM dependency intelligence for any build tool using Maven Central Repository. Includes Context7 integration for upgrade documentation and guidance. icon: https://avatars.githubusercontent.com/u/18013523?s=200&v=4 source: - project: https://github.com/arvindand/maven-tools-mcp \ No newline at end of file + project: https://github.com/arvindand/maven-tools-mcp + commit: d419a7bfeb19f3515838a64f9a695fe38291e0ec diff --git a/servers/mcp-api-gateway/server.yaml b/servers/mcp-api-gateway/server.yaml index b7b8812e..15cf4f25 100644 --- a/servers/mcp-api-gateway/server.yaml +++ b/servers/mcp-api-gateway/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/182288589?s=200&v=4 source: project: https://github.com/rflpazini/mcp-api-gateway + commit: f197f8db4f4990d76148342e27f005d9022df417 config: description: Configure the connection to mcp-api-gateway env: diff --git a/servers/mcp-code-interpreter/server.yaml b/servers/mcp-code-interpreter/server.yaml index 5ac38313..5633e50e 100644 --- a/servers/mcp-code-interpreter/server.yaml +++ b/servers/mcp-code-interpreter/server.yaml @@ -11,6 +11,7 @@ about: icon: https://raw.githubusercontent.com/akuadane/mcp-code-interpreter/refs/heads/main/asset/logo.png source: project: https://github.com/akuadane/mcp-code-interpreter + commit: 209ddfca8c7a20cd44834a6217dfca358b532f90 run: volumes: - notebooks:/app/notebooks diff --git a/servers/mcp-discord/server.yaml b/servers/mcp-discord/server.yaml index 0af83924..6564a3fb 100644 --- a/servers/mcp-discord/server.yaml +++ b/servers/mcp-discord/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/slimslenderslacks/mcp-discord branch: slim/docker + commit: ab1e64668681448e1e7f3f63ad28d9ed54288508 config: secrets: - name: discord.token diff --git a/servers/mcp-github-pr-issue-analyser/server.yaml b/servers/mcp-github-pr-issue-analyser/server.yaml index 74c562ec..c884e3ac 100644 --- a/servers/mcp-github-pr-issue-analyser/server.yaml +++ b/servers/mcp-github-pr-issue-analyser/server.yaml @@ -15,6 +15,7 @@ about: source: project: https://github.com/saidsef/mcp-github-pr-issue-analyser branch: main + commit: eaa15813df0afcc8cf0142b02a1fae0fe0b4e889 config: description: Configure the connection to MCP GitHub PRs Issues Analyser env: diff --git a/servers/mcp-hackernews/server.yaml b/servers/mcp-hackernews/server.yaml index 3cfd30c9..efd44837 100644 --- a/servers/mcp-hackernews/server.yaml +++ b/servers/mcp-hackernews/server.yaml @@ -11,3 +11,4 @@ about: icon: https://news.ycombinator.com/y18.svg source: project: https://github.com/AayushBahukhandi/hackernews-mcp + commit: b3d01052c2bc1f01d890b4e5d0d22a55fe5dadcd diff --git a/servers/mcp-meta-analysis-r/server.yaml b/servers/mcp-meta-analysis-r/server.yaml deleted file mode 100644 index fa5b7e65..00000000 --- a/servers/mcp-meta-analysis-r/server.yaml +++ /dev/null @@ -1,65 +0,0 @@ -name: mcp-meta-analysis-r -image: mmrech/mcp-meta-analysis-r:latest -type: server -meta: - category: data-science - tags: - - statistics - - meta-analysis - - research - - healthcare - - r-statistics - - data-science - - clinical-trials -about: - title: Meta-Analysis MCP Server - description: | - A professional MCP server for conducting meta-analyses with automated statistical validation, R integration, and publication-ready outputs. - - Features: - • Guided workflow for meta-analysis from data import to report generation - • Support for multiple effect measures (OR, RR, MD, SMD, HR) - • Automated statistical validation and data quality checks - • Forest plots and funnel plots with customizable styles - • Publication bias assessment (Egger's test, Begg's test, trim-and-fill) - • Heterogeneity analysis (I², Q-test, τ²) - • Support for CSV, Excel, and RevMan data formats - • Comprehensive report generation (HTML/PDF/Word) - • Session-based workflow management - • Containerized R environment with meta, metafor packages - - Perfect for researchers, clinicians, and data scientists conducting systematic reviews and meta-analyses. - icon: 📊 -source: - project: https://github.com/matheus-rech/mcp-meta-analysis-with-r -run: - volumes: - - '{{meta-analysis.sessions_path}}:/app/user_sessions' - - '{{meta-analysis.logs_path}}:/app/logs' -config: - description: Configure paths for session data and logs - env: - - name: NODE_ENV - example: production - value: 'production' - - name: LOG_LEVEL - example: info - value: 'info' - - name: USE_DOCKER - example: 'false' - value: 'true' - description: Use Docker for R execution (set to false if R is installed locally) - parameters: - type: object - properties: - sessions_path: - type: string - description: Directory path where session data will be stored - default: /Users/local-test/meta-analysis-sessions - logs_path: - type: string - description: Directory path where logs will be stored - default: /Users/local-test/meta-analysis-logs - required: - - sessions_path - - logs_path \ No newline at end of file diff --git a/servers/mcp-python-refactoring/server.yaml b/servers/mcp-python-refactoring/server.yaml index f4602bc0..ad6c3494 100644 --- a/servers/mcp-python-refactoring/server.yaml +++ b/servers/mcp-python-refactoring/server.yaml @@ -31,5 +31,6 @@ about: icon: https://avatars.githubusercontent.com/u/2664231?s=200&v=4 source: project: https://github.com/slamer59/mcp-python-refactoring + commit: b66781d58674cd066e4ed8786215ab44ecc0b14b config: - description: Python Refactoring Assistant - No additional configuration required \ No newline at end of file + description: Python Refactoring Assistant - No additional configuration required diff --git a/servers/mcp-reddit/server.yaml b/servers/mcp-reddit/server.yaml index ebb2bf30..e46a91ce 100644 --- a/servers/mcp-reddit/server.yaml +++ b/servers/mcp-reddit/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/135265106?v=4 source: project: https://github.com/KrishnaRandad2023/mcp-reddit + commit: 2c9b356a32d14f2c1708415966ce289133477f74 config: description: Configure the connection to Mcp reddit secrets: diff --git a/servers/memory/server.yaml b/servers/memory/server.yaml index e98da56c..d37db5ee 100644 --- a/servers/memory/server.yaml +++ b/servers/memory/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 directory: src/memory run: volumes: diff --git a/servers/meta-ads/readme.md b/servers/meta-ads/readme.md deleted file mode 100644 index 6309a6c5..00000000 --- a/servers/meta-ads/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://github.com/pipeboard-co/meta-ads-mcp diff --git a/servers/meta-ads/server.yaml b/servers/meta-ads/server.yaml deleted file mode 100644 index 4855d42a..00000000 --- a/servers/meta-ads/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: meta-ads -type: remote -dynamic: - tools: true -meta: - category: marketing - tags: - - marketing - - advertising - - meta - - remote -about: - title: Meta Ads by Pipeboard - description: Advertising - icon: https://www.google.com/s2/favicons?domain=pipeboard.co&sz=64 -remote: - transport_type: sse - url: https://mcp.pipeboard.co/sse -oauth: - - provider: meta-ads - secret: meta-ads.personal_access_token - env: META_ADS_PERSONAL_ACCESS_TOKEN diff --git a/servers/meta-ads/tools.json b/servers/meta-ads/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/meta-ads/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/metabase/server.yaml b/servers/metabase/server.yaml index dfc39daa..12c757b1 100644 --- a/servers/metabase/server.yaml +++ b/servers/metabase/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/easecloudio/mcp-metabase-server branch: v1.0.2 + commit: 8a4dfce8afd45ead9cc4c2764f41ace6f91adf66 config: description: Configure the connection to MetaBase MCP secrets: @@ -24,10 +25,10 @@ config: env: - name: METABASE_URL example: https://app.metabase.com - value: "{{metabase.metabase_url}}" + value: "{{metabase.metabaseurl}}" - name: METABASE_USERNAME example: youruser - value: "{{metabase.metabase_username}}" + value: "{{metabase.metabaseusername}}" parameters: type: object properties: diff --git a/servers/microsoft-learn/tools.json b/servers/microsoft-learn/tools.json new file mode 100644 index 00000000..aba7193e --- /dev/null +++ b/servers/microsoft-learn/tools.json @@ -0,0 +1,40 @@ +[ + { + "name": "microsoft_docs_search", + "description": "Performs semantic search against Microsoft official technical documentation", + "arguments": [ + { + "name": "query", + "type": "string", + "desc": "The search query for retrieval" + } + ] + }, + { + "name": "microsoft_docs_fetch", + "description": "Fetch and convert a Microsoft documentation page into markdown format", + "arguments": [ + { + "name": "url", + "type": "string", + "desc": "URL of the documentation page to read" + } + ] + }, + { + "name": "microsoft_code_sample_search", + "description": "Search for official Microsoft code snippets and examples", + "arguments": [ + { + "name": "query", + "type": "string", + "desc": "Search query for Microsoft code snippets" + }, + { + "name": "language", + "type": "string", + "desc": "Optional programming language filter" + } + ] + } +] diff --git a/servers/minecraft-wiki/server.yaml b/servers/minecraft-wiki/server.yaml index 2e9345bc..0eefa951 100644 --- a/servers/minecraft-wiki/server.yaml +++ b/servers/minecraft-wiki/server.yaml @@ -12,5 +12,6 @@ about: icon: https://cdn.jsdelivr.net/npm/simple-icons@v7/icons/minecraft.svg source: project: https://github.com/L3-N0X/Minecraft-Wiki-MCP + commit: 7a753f50eab0ddf1f743d1cb1ab7edb7a9737ad2 config: description: Configure the connection to Minecraft Wiki Server diff --git a/servers/monday/server.yaml b/servers/monday/server.yaml index 18004ab6..ea4f87d7 100644 --- a/servers/monday/server.yaml +++ b/servers/monday/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: monday.com - description: Productivity + description: Manage projects, workflows, and team collaboration with AI-powered automation and customizable boards icon: https://www.google.com/s2/favicons?domain=monday.com&sz=64 remote: transport_type: sse diff --git a/servers/mongodb/server.yaml b/servers/mongodb/server.yaml index d71325de..750d5eac 100644 --- a/servers/mongodb/server.yaml +++ b/servers/mongodb/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/11214950?v=4 source: project: https://github.com/mongodb-js/mongodb-mcp-server + commit: b9aa6840125a7429ad4b960161431f0fda685c16 config: description: Configure the connection to MongoDB secrets: diff --git a/servers/multiversx-mx/server.yaml b/servers/multiversx-mx/server.yaml index 99deb200..47373a71 100644 --- a/servers/multiversx-mx/server.yaml +++ b/servers/multiversx-mx/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/114073177?s=200&v=4 source: project: https://github.com/multiversx/mx-mcp + commit: 7ed06c2bbc7b71764e55759f057c2da6aab55e98 config: description: Configure the connection to MultiversX blockchain env: diff --git a/servers/nasdaq-data-link/server.yaml b/servers/nasdaq-data-link/server.yaml index c3261be2..a821f805 100644 --- a/servers/nasdaq-data-link/server.yaml +++ b/servers/nasdaq-data-link/server.yaml @@ -14,6 +14,7 @@ about: icon: https://raw.githubusercontent.com/stefanoamorelli/nasdaq-data-link-mcp/refs/heads/main/nasdaq-mcp-server-logo.png source: project: https://github.com/stefanoamorelli/nasdaq-data-link-mcp + commit: 758499d271b925e4eae510c5b958fa317fce76b0 config: secrets: - name: nasdaq_data_link_api_key diff --git a/servers/needle-mcp/server.yaml b/servers/needle-mcp/server.yaml index e8dd12fe..59b80a39 100644 --- a/servers/needle-mcp/server.yaml +++ b/servers/needle-mcp/server.yaml @@ -16,9 +16,10 @@ about: icon: https://avatars.githubusercontent.com/u/194782563?s=200&v=4 source: project: https://github.com/needle-ai/needle-mcp + commit: 96d82632e3c719ffa61dc8f0909a7cc0d185a161 config: description: Configure the connection to Needle secrets: - name: needle-mcp.api_key env: NEEDLE_API_KEY - example: your-needle-api-key-here \ No newline at end of file + example: your-needle-api-key-here diff --git a/servers/neo4j-cloud-aura-api/server.yaml b/servers/neo4j-cloud-aura-api/server.yaml index da8613ca..5361e95a 100644 --- a/servers/neo4j-cloud-aura-api/server.yaml +++ b/servers/neo4j-cloud-aura-api/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/916372?s=200&v=4 source: project: https://github.com/neo4j-contrib/mcp-neo4j + commit: 8e104e33b5e2cd8c3a9b2ec5f1603c1a960ad661 directory: servers/mcp-neo4j-cloud-aura-api config: description: Configure the connection to Neo4j Aura diff --git a/servers/neo4j-cypher/server.yaml b/servers/neo4j-cypher/server.yaml index c7d76c2e..43b80c3f 100644 --- a/servers/neo4j-cypher/server.yaml +++ b/servers/neo4j-cypher/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/916372?s=200&v=4 source: project: https://github.com/neo4j-contrib/mcp-neo4j + commit: 8e104e33b5e2cd8c3a9b2ec5f1603c1a960ad661 directory: servers/mcp-neo4j-cypher config: description: Configure the connection to Neo4j diff --git a/servers/neo4j-data-modeling/server.yaml b/servers/neo4j-data-modeling/server.yaml index cf35c132..56c8e67b 100644 --- a/servers/neo4j-data-modeling/server.yaml +++ b/servers/neo4j-data-modeling/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/916372?s=200&v=4 source: project: https://github.com/neo4j-contrib/mcp-neo4j + commit: 8e104e33b5e2cd8c3a9b2ec5f1603c1a960ad661 directory: servers/mcp-neo4j-data-modeling config: description: Optional configuration to deploy MCP server @@ -49,4 +50,4 @@ config: type: string server_allowed_hosts: type: string -requirement: neo4j \ No newline at end of file +requirement: neo4j diff --git a/servers/neo4j-memory/server.yaml b/servers/neo4j-memory/server.yaml index d373f8b7..6df4bdb3 100644 --- a/servers/neo4j-memory/server.yaml +++ b/servers/neo4j-memory/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/916372?s=200&v=4 source: project: https://github.com/neo4j-contrib/mcp-neo4j + commit: 8e104e33b5e2cd8c3a9b2ec5f1603c1a960ad661 directory: servers/mcp-neo4j-memory config: description: Configure the connection to Neo4j diff --git a/servers/neon-remote/readme.md b/servers/neon-remote/readme.md deleted file mode 100644 index 82d0396b..00000000 --- a/servers/neon-remote/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://neon.com/blog/announcing-neons-remote-mcp-server diff --git a/servers/neon-remote/server.yaml b/servers/neon-remote/server.yaml deleted file mode 100644 index b35cbbfa..00000000 --- a/servers/neon-remote/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: neon-remote -type: remote -dynamic: - tools: true -meta: - category: database - tags: - - database - - postgres - - serverless - - remote -about: - title: Neon - description: Software Development - icon: https://www.google.com/s2/favicons?domain=neon.tech&sz=64 -remote: - transport_type: sse - url: https://mcp.neon.tech/sse -oauth: - - provider: neon-remote - secret: neon-remote.personal_access_token - env: NEON_REMOTE_PERSONAL_ACCESS_TOKEN diff --git a/servers/neon-remote/tools.json b/servers/neon-remote/tools.json deleted file mode 100644 index de07d13a..00000000 --- a/servers/neon-remote/tools.json +++ /dev/null @@ -1,57 +0,0 @@ -[ - { - "name": "list_projects", - "description": "Retrieve list of Neon projects with search and limit options", - "arguments": [] - }, - { - "name": "list_shared_projects", - "description": "List projects shared with current user", - "arguments": [] - }, - { - "name": "describe_project", - "description": "Get detailed project information", - "arguments": [] - }, - { - "name": "create_project", - "description": "Create new Neon projects", - "arguments": [] - }, - { - "name": "delete_project", - "description": "Delete projects and associated resources", - "arguments": [] - }, - { - "name": "create_branch", - "description": "Create new branches within projects", - "arguments": [] - }, - { - "name": "reset_from_parent", - "description": "Reset branch to parent branch state", - "arguments": [] - }, - { - "name": "run_sql", - "description": "Execute SQL queries", - "arguments": [] - }, - { - "name": "create_table", - "description": "Create database tables", - "arguments": [] - }, - { - "name": "update_row", - "description": "Update database rows", - "arguments": [] - }, - { - "name": "delete_table", - "description": "Delete database tables", - "arguments": [] - } -] \ No newline at end of file diff --git a/servers/neon/server.yaml b/servers/neon/server.yaml index 7590fe91..a7ff3a87 100644 --- a/servers/neon/server.yaml +++ b/servers/neon/server.yaml @@ -11,7 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/183852044?s=200&v=4 source: project: https://github.com/neondatabase-labs/mcp-server-neon - branch: dbfa184afd9fc677c0d6b007a62b33194e883821 + commit: dbfa184afd9fc677c0d6b007a62b33194e883821 config: secrets: - name: neon.api_key diff --git a/servers/netlify/readme.md b/servers/netlify/readme.md deleted file mode 100644 index 6e486340..00000000 --- a/servers/netlify/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://docs.netlify.com/build/build-with-ai/netlify-mcp-server/ diff --git a/servers/netlify/server.yaml b/servers/netlify/server.yaml deleted file mode 100644 index 75f2721d..00000000 --- a/servers/netlify/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: netlify -type: remote -dynamic: - tools: true -meta: - category: devops - tags: - - devops - - hosting - - deployment - - remote -about: - title: Netlify - description: Software Development - icon: https://www.google.com/s2/favicons?domain=netlify.com&sz=64 -remote: - transport_type: sse - url: https://mcp.netlify.com/sse -oauth: - - provider: netlify - secret: netlify.personal_access_token - env: NETLIFY_PERSONAL_ACCESS_TOKEN diff --git a/servers/netlify/tools.json b/servers/netlify/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/netlify/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/next-devtools-mcp/server.yaml b/servers/next-devtools-mcp/server.yaml new file mode 100644 index 00000000..2f29ba91 --- /dev/null +++ b/servers/next-devtools-mcp/server.yaml @@ -0,0 +1,15 @@ +name: next-devtools-mcp +image: mcp/next-devtools-mcp +type: server +meta: + category: devtools + tags: + - devtools + - nextjs +about: + title: Next.js DevTools MCP + description: next-devtools-mcp is a Model Context Protocol (MCP) server that provides Next.js development tools and utilities for AI coding assistants. + icon: https://avatars.githubusercontent.com/u/14985020?s=200&v=4 +source: + project: https://github.com/kgprs/next-devtools-mcp + commit: 9ed2aab35ef34d7e19707cf19fb49d6bfc1936f5 diff --git a/servers/node-code-sandbox/server.yaml b/servers/node-code-sandbox/server.yaml index 3767ce41..205ba809 100644 --- a/servers/node-code-sandbox/server.yaml +++ b/servers/node-code-sandbox/server.yaml @@ -12,3 +12,4 @@ about: source: project: https://github.com/alfonsograziano/node-code-sandbox-mcp branch: master + commit: f6727b3be251fbd24826ca961a16cd98bbfe6125 diff --git a/servers/notion-remote/server.yaml b/servers/notion-remote/server.yaml index 7d9b2790..4ace426b 100644 --- a/servers/notion-remote/server.yaml +++ b/servers/notion-remote/server.yaml @@ -11,11 +11,11 @@ meta: - remote about: title: Notion - description: Project Management + description: Manage projects, create documentation, and organize information in collaborative workspaces icon: https://www.google.com/s2/favicons?domain=notion.so&sz=64 remote: - transport_type: sse - url: https://mcp.notion.so/sse + transport_type: streamable-http + url: https://mcp.notion.com/mcp oauth: - provider: notion-remote secret: notion-remote.personal_access_token diff --git a/servers/notion/server.yaml b/servers/notion/server.yaml index 70ddc13f..7fc44a00 100644 --- a/servers/notion/server.yaml +++ b/servers/notion/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/4792552?s=200&v=4 source: project: https://github.com/makenotion/notion-mcp-server + commit: ffc1b18807df0cd72717f6ba55e7866af2d91ccd run: env: OPENAPI_MCP_HEADERS: '{"Authorization": "Bearer $INTERNAL_INTEGRATION_TOKEN", "Notion-Version": "2022-06-28"}' diff --git a/servers/novita/server.yaml b/servers/novita/server.yaml index 5a4c6c02..88da0b9f 100644 --- a/servers/novita/server.yaml +++ b/servers/novita/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/145963681?v=4 source: project: https://github.com/novitalabs/novita-mcp-server + commit: 64efc99e8b05d05ddd1e3513f5f21f6aa236e82d config: env: - name: NOVITA_API_KEY diff --git a/servers/npm-sentinel/server.yaml b/servers/npm-sentinel/server.yaml index 4c92f43a..f084d479 100644 --- a/servers/npm-sentinel/server.yaml +++ b/servers/npm-sentinel/server.yaml @@ -12,3 +12,4 @@ about: icon: https://avatars.githubusercontent.com/u/85507589?v=4 source: project: https://github.com/Nekzus/npm-sentinel-mcp + commit: a1bd358e4348d7dd4a8fe9a4ab091fc34e0397a8 diff --git a/servers/obsidian/server.yaml b/servers/obsidian/server.yaml index c9d9cb3e..7d515f77 100644 --- a/servers/obsidian/server.yaml +++ b/servers/obsidian/server.yaml @@ -13,6 +13,7 @@ source: project: https://github.com/docker/mcp-obsidian upstream: https://github.com/MarkusPfundstein/mcp-obsidian branch: docker-support + commit: b20ad46eea43988a4c1a6f8ec7947ef4c6cbf9a9 config: description: Configure the connection to Obsidian secrets: diff --git a/servers/octagon/server.yaml b/servers/octagon/server.yaml index 4a0853f2..f884fa05 100644 --- a/servers/octagon/server.yaml +++ b/servers/octagon/server.yaml @@ -11,10 +11,10 @@ meta: - remote about: title: Octagon - description: Market Intelligence + description: Access comprehensive market intelligence, analyze SEC filings, earnings transcripts, and financial data to uncover investment opportunities icon: https://www.google.com/s2/favicons?domain=octagonai.co&sz=64 remote: - transport_type: sse + transport_type: streamable-http url: https://mcp.octagonagents.com/mcp oauth: - provider: octagon diff --git a/servers/okta-mcp-fctr/server.yaml b/servers/okta-mcp-fctr/server.yaml index dcf69681..8bcd6d52 100644 --- a/servers/okta-mcp-fctr/server.yaml +++ b/servers/okta-mcp-fctr/server.yaml @@ -19,6 +19,7 @@ about: icon: https://avatars.githubusercontent.com/u/198773578?v=4 source: project: https://github.com/fctr-id/okta-mcp-server + commit: 39b4151a2ece178640fc19ccbb045346ad761e53 config: description: Configure the connection to Okta secrets: diff --git a/servers/omi/server.yaml b/servers/omi/server.yaml index 7757ee4d..877a685a 100644 --- a/servers/omi/server.yaml +++ b/servers/omi/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/162546372?v=4 source: project: https://github.com/BasedHardware/omi + commit: e4b42efc1e94a08a76228e0928e1a3245431d7b2 directory: mcp config: description: Configure the connection to omi-mcp diff --git a/servers/onecontext/readme.md b/servers/onecontext/readme.md deleted file mode 100644 index af91a0c8..00000000 --- a/servers/onecontext/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://docs.onecontext.ai diff --git a/servers/onecontext/server.yaml b/servers/onecontext/server.yaml deleted file mode 100644 index 68a6f999..00000000 --- a/servers/onecontext/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: onecontext -type: remote -dynamic: - tools: true -meta: - category: ai - tags: - - ai - - rag - - context - - remote -about: - title: OneContext - description: RAG-as-a-Service - icon: https://www.google.com/s2/favicons?domain=onecontext.ai&sz=64 -remote: - transport_type: sse - url: https://mcp.onecontext.ai/sse -oauth: - - provider: onecontext - secret: onecontext.personal_access_token - env: ONECONTEXT_PERSONAL_ACCESS_TOKEN diff --git a/servers/onecontext/tools.json b/servers/onecontext/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/onecontext/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/onlyoffice-docspace/server.yaml b/servers/onlyoffice-docspace/server.yaml index 764759a5..3633c577 100644 --- a/servers/onlyoffice-docspace/server.yaml +++ b/servers/onlyoffice-docspace/server.yaml @@ -13,6 +13,7 @@ about: icon: https://avatars.githubusercontent.com/u/1426033?v=4 source: project: https://github.com/ONLYOFFICE/docspace-mcp + commit: 7f192dfe7f9ab9c464cd8716c5f02de9b48e6c62 config: description: Configure the connection to ONLYOFFICE DocSpace secrets: diff --git a/servers/openapi-schema/server.yaml b/servers/openapi-schema/server.yaml index 12d328f2..d56219a3 100644 --- a/servers/openapi-schema/server.yaml +++ b/servers/openapi-schema/server.yaml @@ -13,6 +13,14 @@ source: project: https://github.com/slimslenderslacks/mcp-openapi-schema upstream: https://github.com/hannesj/mcp-openapi-schema branch: master + commit: 6f14ab86576b7581bc83ec7721dd6d95d5e1973d run: volumes: - - '{{openApiSchemaPath|or:[]|volume|into}}' + - '{{openapi-schema.SchemaPath|or:[]|volume|into}}' +config: + description: Expose OpenAPI schema information to Large Language Models + parameters: + type: object + properties: + SchemaPath: + type: string diff --git a/servers/openapi/server.yaml b/servers/openapi/server.yaml index 84361112..239402a4 100644 --- a/servers/openapi/server.yaml +++ b/servers/openapi/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/lepoco/openapi.client branch: main + commit: 30bc5dfc95c789c0dadecf4816dcbf641b49aa3b config: description: Configure the connection to OpenAPI Toolkit for MCP env: diff --git a/servers/openbnb-airbnb/server.yaml b/servers/openbnb-airbnb/server.yaml index 5c47cc4b..fba9fe8f 100644 --- a/servers/openbnb-airbnb/server.yaml +++ b/servers/openbnb-airbnb/server.yaml @@ -12,4 +12,5 @@ about: icon: https://avatars.githubusercontent.com/u/202564951?v=4 source: project: https://github.com/openbnb-org/mcp-server-airbnb + commit: 57d9d6cdabe813c4d4ab57f90481490aaf8b1b0a diff --git a/servers/openmesh/server.yaml b/servers/openmesh/server.yaml index e5c3ff13..c28942b5 100644 --- a/servers/openmesh/server.yaml +++ b/servers/openmesh/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: OpenMesh - description: Marketplace for MCP Servers + description: Discover and connect to a curated marketplace of MCP servers for extending AI agent capabilities icon: https://www.google.com/s2/favicons?domain=openmesh.network&sz=64 remote: transport_type: streamable-http diff --git a/servers/openweather/server.yaml b/servers/openweather/server.yaml index 0f8ed2ec..737d6d4b 100644 --- a/servers/openweather/server.yaml +++ b/servers/openweather/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/1743227?s=200&v=4 source: project: https://github.com/mschneider82/mcp-openweather + commit: e032683574a0723591445462ef7104d360ad0889 config: description: Configure the connection to TODO secrets: diff --git a/servers/opik/server.yaml b/servers/opik/server.yaml index 68ba0ac5..3b43d13b 100644 --- a/servers/opik/server.yaml +++ b/servers/opik/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/31487821?s=200&v=4 source: project: https://github.com/comet-ml/opik-mcp + commit: 40b8e53d73f363ebcd25085a5ff9274c3ce79718 config: description: Configure the connection to Opik secrets: diff --git a/servers/opine-mcp-server/server.yaml b/servers/opine-mcp-server/server.yaml index 3a66e126..062e1780 100644 --- a/servers/opine-mcp-server/server.yaml +++ b/servers/opine-mcp-server/server.yaml @@ -12,6 +12,7 @@ about: icon: https://app.tryopine.com/_next/image?url=%2Flogomark.png&w=32&q=75 source: project: https://github.com/cody-docker/opine-mcp-server + commit: 63e0c626850dad818491a04c84f28e575e530322 config: description: Configure the connection to Opine MCP Server secrets: diff --git a/servers/oracle/server.yaml b/servers/oracle/server.yaml index f2ab1caa..b750d7ee 100644 --- a/servers/oracle/server.yaml +++ b/servers/oracle/server.yaml @@ -14,6 +14,7 @@ about: icon: https://avatars.githubusercontent.com/u/93792006?v=4 source: project: https://github.com/samscarrow/oracle-mcp-server + commit: 1aa0b0a2f859879495bcd11d91a5637207d72f6d config: description: Configure the connection to Oracle Database secrets: diff --git a/servers/osp_marketing_tools/server.yaml b/servers/osp_marketing_tools/server.yaml index 73437b94..036e0f7e 100644 --- a/servers/osp_marketing_tools/server.yaml +++ b/servers/osp_marketing_tools/server.yaml @@ -11,3 +11,4 @@ about: icon: https://avatars.githubusercontent.com/u/47528973?s=200&v=4 source: project: https://github.com/open-strategy-partners/osp_marketing_tools + commit: 83f55198c4d4e7b315565f00792f63373ff9cc67 diff --git a/servers/oxylabs/server.yaml b/servers/oxylabs/server.yaml index 2807a360..ada992b8 100644 --- a/servers/oxylabs/server.yaml +++ b/servers/oxylabs/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/43960873?s=200&v=4 source: project: https://github.com/oxylabs/oxylabs-mcp + commit: 0e738c89335e8e521a5b1b925f048ea075c8235b config: description: Configure the connection to Oxylabs Scraper secrets: diff --git a/servers/paper-search/server.yaml b/servers/paper-search/server.yaml index cbcaf962..2b901437 100644 --- a/servers/paper-search/server.yaml +++ b/servers/paper-search/server.yaml @@ -11,3 +11,4 @@ about: icon: https://avatars.githubusercontent.com/u/197887910?v=4 source: project: https://github.com/openags/paper-search-mcp + commit: cf2697fd04a7b7c1ced0e382ab84f0c214614f83 diff --git a/servers/paypal/server.yaml b/servers/paypal/server.yaml index e7d1469a..e78b4d2b 100644 --- a/servers/paypal/server.yaml +++ b/servers/paypal/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: PayPal - description: Payments + description: Process online payments, manage transactions, and integrate PayPal's payment solutions into applications icon: https://www.google.com/s2/favicons?domain=paypal.com&sz=64 remote: transport_type: sse diff --git a/servers/perplexity-ask/server.yaml b/servers/perplexity-ask/server.yaml index 44157e47..12f8e3c0 100644 --- a/servers/perplexity-ask/server.yaml +++ b/servers/perplexity-ask/server.yaml @@ -12,7 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/110299016?s=200&v=4 source: project: https://github.com/ppl-ai/modelcontextprotocol - branch: f0a927c250e04b389ff5c34f6a2a85ad625668e8 + commit: f0a927c250e04b389ff5c34f6a2a85ad625668e8 dockerfile: perplexity-ask/Dockerfile config: description: Configure the connection to Perplexity diff --git a/servers/pia/server.yaml b/servers/pia/server.yaml index 5ac09d3b..096ef2b1 100644 --- a/servers/pia/server.yaml +++ b/servers/pia/server.yaml @@ -13,6 +13,7 @@ about: icon: http://programintegrity.org/wp-content/uploads/2025/08/docker-mcp-pia-icon-512.png source: project: https://github.com/Program-Integrity-Alliance/pia-mcp-local + commit: f04a7520f5a5d3d8db6f7deb7f0a093a60681506 config: description: Configure the connection to Program Integrity Alliance (PIA) MCP Server. You can get a key here https://mcp.programintegrity.org/register secrets: diff --git a/servers/pinecone/server.yaml b/servers/pinecone/server.yaml index 0e767ddb..b9736933 100644 --- a/servers/pinecone/server.yaml +++ b/servers/pinecone/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/54333248?v=4 source: project: https://github.com/pinecone-io/assistant-mcp + commit: 7e32e35c20fe3f79d2504d4371f28e1b1eb34009 config: description: Configure the connection to Pinecone Assistant secrets: diff --git a/servers/plaid/readme.md b/servers/plaid/readme.md deleted file mode 100644 index cb9287da..00000000 --- a/servers/plaid/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://plaid.com/blog/anthropic-api-mcp/ diff --git a/servers/plaid/server.yaml b/servers/plaid/server.yaml deleted file mode 100644 index 616f0bae..00000000 --- a/servers/plaid/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: plaid -type: remote -dynamic: - tools: true -meta: - category: fintech - tags: - - fintech - - banking - - financial-data - - remote -about: - title: Plaid - description: Payments - icon: https://www.google.com/s2/favicons?domain=plaid.com&sz=64 -remote: - transport_type: sse - url: https://api.dashboard.plaid.com/mcp/sse -oauth: - - provider: plaid - secret: plaid.personal_access_token - env: PLAID_PERSONAL_ACCESS_TOKEN diff --git a/servers/plaid/tools.json b/servers/plaid/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/plaid/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/playwright-mcp-server/server.yaml b/servers/playwright-mcp-server/server.yaml index b46c940a..8474e503 100644 --- a/servers/playwright-mcp-server/server.yaml +++ b/servers/playwright-mcp-server/server.yaml @@ -11,6 +11,14 @@ about: icon: https://avatars.githubusercontent.com/u/10337030?v=4 source: project: https://github.com/executeautomation/mcp-playwright + commit: 51b2725f06f6df703f797f4025cac4c5f3a71f05 run: volumes: - - '{{playwright-mcp-server.data}}:{{playwright-mcp-server.}}' + - '{{playwright-mcp-server.data}}:{{playwright-mcp-server.data}}' +config: + description: data location + parameters: + type: object + properties: + data: + type: string diff --git a/servers/playwright/server.yaml b/servers/playwright/server.yaml index 10a671bb..a18ea69c 100644 --- a/servers/playwright/server.yaml +++ b/servers/playwright/server.yaml @@ -12,3 +12,4 @@ about: icon: https://avatars.githubusercontent.com/u/6154722?v=4 source: project: https://github.com/microsoft/playwright-mcp + commit: fb900a88272be24fce180794086b5cc96615174a diff --git a/servers/pluggedin-mcp-proxy/server.yaml b/servers/pluggedin-mcp-proxy/server.yaml index a204f34c..b4918191 100644 --- a/servers/pluggedin-mcp-proxy/server.yaml +++ b/servers/pluggedin-mcp-proxy/server.yaml @@ -15,6 +15,7 @@ about: icon: https://www.plugged.in/favicon.ico source: project: https://github.com/VeriTeknik/pluggedin-mcp-proxy + commit: 38a2ef3076a9af4cdc1a8a2dc213f6aba1876570 config: description: Configure your Plugged.in API connection. Get your API key from https://plugged.in in the API Keys section. secrets: @@ -32,4 +33,4 @@ config: pluggedin_api_base_url: type: string default: https://plugged.in - description: Base URL for the Plugged.in API (optional, defaults to https://plugged.in for cloud or http://localhost:12005 for self-hosted) \ No newline at end of file + description: Base URL for the Plugged.in API (optional, defaults to https://plugged.in for cloud or http://localhost:12005 for self-hosted) diff --git a/servers/pomodash/server.yaml b/servers/pomodash/server.yaml index 96ebb735..cf73bbe6 100644 --- a/servers/pomodash/server.yaml +++ b/servers/pomodash/server.yaml @@ -19,6 +19,7 @@ about: source: project: https://github.com/dannyytv/pomodash-mcp-server + commit: 3c24d42aef5dda0e3b0006b2e04712843aff1d70 config: diff --git a/servers/postgres/server.yaml b/servers/postgres/server.yaml index e176c58d..209c2f4a 100644 --- a/servers/postgres/server.yaml +++ b/servers/postgres/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 dockerfile: src/postgres/Dockerfile run: command: diff --git a/servers/postman/server.yaml b/servers/postman/server.yaml index d7ece5b2..acdb3806 100644 --- a/servers/postman/server.yaml +++ b/servers/postman/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/postmanlabs/postman-mcp-server branch: main + commit: 61a7c68190159e60cdb65923f44f8c096c9da638 config: description: Configure the connection to Postman MCP server secrets: diff --git a/servers/pref-editor/server.yaml b/servers/pref-editor/server.yaml index 9fbbd814..4f416213 100644 --- a/servers/pref-editor/server.yaml +++ b/servers/pref-editor/server.yaml @@ -14,3 +14,4 @@ about: icon: https://raw.githubusercontent.com/charlesmuchene/pref-editor-mcp-server/refs/heads/main/logo.png source: project: https://github.com/charlesmuchene/pref-editor-mcp-server + commit: cd93e23e74b81d29bf2082bc10a79319ff32986a diff --git a/servers/prisma-postgres/server.yaml b/servers/prisma-postgres/server.yaml index 5bf38060..abc95a47 100644 --- a/servers/prisma-postgres/server.yaml +++ b/servers/prisma-postgres/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Prisma Postgres - description: Database + description: Access PostgreSQL databases through Prisma's type-safe ORM with automated migrations and intuitive query building icon: https://www.google.com/s2/favicons?domain=prisma.io&sz=64 remote: transport_type: streamable-http diff --git a/servers/prometheus/server.yaml b/servers/prometheus/server.yaml index f608738f..fa99a083 100644 --- a/servers/prometheus/server.yaml +++ b/servers/prometheus/server.yaml @@ -18,6 +18,7 @@ about: icon: https://upload.wikimedia.org/wikipedia/commons/3/38/Prometheus_software_logo.svg source: project: https://github.com/pab1it0/prometheus-mcp-server + commit: 219d4f0772cfb035d702e1d7c64643baf60b3479 config: description: Configure the connection to your Prometheus server env: diff --git a/servers/pulumi-remote/readme.md b/servers/pulumi-remote/readme.md new file mode 100644 index 00000000..1bd37a37 --- /dev/null +++ b/servers/pulumi-remote/readme.md @@ -0,0 +1 @@ +Docs: https://www.pulumi.com/docs/iac/guides/ai-integration/mcp-server/ diff --git a/servers/pulumi-remote/server.yaml b/servers/pulumi-remote/server.yaml new file mode 100644 index 00000000..cc6832bf --- /dev/null +++ b/servers/pulumi-remote/server.yaml @@ -0,0 +1,20 @@ +name: pulumi-remote +type: remote +dynamic: + tools: true +meta: + category: devops + tags: + - devops + - remote +about: + title: Pulumi + description: Create, deploy, and manage cloud infrastructure using your favorite language. + icon: https://www.google.com/s2/favicons?domain=pulumi.com&sz=64 +remote: + transport_type: streamable-http + url: https://mcp.ai.pulumi.com/mcp +oauth: + - provider: pulumi-remote + secret: pulumi-remote.personal_access_token + env: PULUMI_REMOTE_PERSONAL_ACCESS_TOKEN diff --git a/servers/pulumi-remote/tools.json b/servers/pulumi-remote/tools.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/servers/pulumi-remote/tools.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/servers/pulumi/server.yaml b/servers/pulumi/server.yaml index 02fcc417..6d5c95f9 100644 --- a/servers/pulumi/server.yaml +++ b/servers/pulumi/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/21992475?s=200&v=4 source: project: https://github.com/pulumi/mcp-server + commit: bb10e6c60943f25b200fc0e7c221f2d1e259aff6 run: command: - stdio diff --git a/servers/puppeteer/server.yaml b/servers/puppeteer/server.yaml index 35d6675b..bfe5e765 100644 --- a/servers/puppeteer/server.yaml +++ b/servers/puppeteer/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 dockerfile: src/puppeteer/Dockerfile config: description: The MCP server is allowed to access these paths diff --git a/servers/quantconnect/server.yaml b/servers/quantconnect/server.yaml index 5d86f354..b200df45 100644 --- a/servers/quantconnect/server.yaml +++ b/servers/quantconnect/server.yaml @@ -12,15 +12,16 @@ about: source: project: https://github.com/QuantConnect/mcp-server branch: master + commit: 3a656b0897adb401fb27b0cfe6be1570da03920f config: description: Configure the connection to QuantConnect MCP Server env: - name: QUANTCONNECT_USER_ID example: "12345678" - value: "{{quantconnect.quantconnect_user_id}}" + value: "{{quantconnect.quantconnectuserid}}" - name: QUANTCONNECT_API_TOKEN example: 61b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90 - value: "{{quantconnect.quantconnect_api_token}}" + value: "{{quantconnect.quantconnectapitoken}}" - name: AGENT_NAME example: MCP Server value: MCP Server diff --git a/servers/ramparts/server.yaml b/servers/ramparts/server.yaml index 913b17de..f9767c3b 100644 --- a/servers/ramparts/server.yaml +++ b/servers/ramparts/server.yaml @@ -15,6 +15,7 @@ about: source: project: https://github.com/getjavelin/ramparts branch: main + commit: f206d33e6fe746aae1d58d00c56073c546189900 dockerfile: MCP-Dockerfile config: description: Configure Ramparts security scanner diff --git a/servers/razorpay/server.yaml b/servers/razorpay/server.yaml index a70524e9..9f846f28 100644 --- a/servers/razorpay/server.yaml +++ b/servers/razorpay/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/7713209?v=4 source: project: https://github.com/razorpay/razorpay-mcp-server + commit: 93bf471d1939bcf1bc2351ae1b20e71c34aafe33 config: description: Configure the connection to Razorpay secrets: diff --git a/servers/redis-cloud/server.yaml b/servers/redis-cloud/server.yaml index 715637cf..be36aa8a 100644 --- a/servers/redis-cloud/server.yaml +++ b/servers/redis-cloud/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/1529926?s=200&v=4 source: project: https://github.com/redis/mcp-redis-cloud + commit: 015aaa6bfeec87198a08eaae00352348d4db4b72 config: description: Configure the connection to Redis Cloud secrets: diff --git a/servers/redis/server.yaml b/servers/redis/server.yaml index ff889db7..e322b117 100644 --- a/servers/redis/server.yaml +++ b/servers/redis/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/1529926?v=4 source: project: https://github.com/redis/mcp-redis + commit: 9db504bb626958cdda5a204118ad673d77b31eff config: description: Configure the connection to Redis secrets: diff --git a/servers/ref/server.yaml b/servers/ref/server.yaml index f35cc588..2772377d 100644 --- a/servers/ref/server.yaml +++ b/servers/ref/server.yaml @@ -12,9 +12,10 @@ about: icon: https://avatars.githubusercontent.com/u/210568548?v=4 source: project: https://github.com/ref-tools/ref-tools-mcp + commit: ccd03425dcd3d7745c9ec97f8b999f19306b821f config: description: Configure the connection to Ref secrets: - name: ref.REF_API_KEY env: REF_API_KEY - example: ref-abcdefghi123 \ No newline at end of file + example: ref-abcdefghi123 diff --git a/servers/render/server.yaml b/servers/render/server.yaml index 75ede265..a12742a8 100644 --- a/servers/render/server.yaml +++ b/servers/render/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/114033652?v=4 source: project: https://github.com/render-oss/render-mcp-server + commit: aa1ad111a4eb856988125c430634d3632a78864f config: description: Configure the connection to Render secrets: diff --git a/servers/resend/server.yaml b/servers/resend/server.yaml index 52c72c6b..afc2d062 100644 --- a/servers/resend/server.yaml +++ b/servers/resend/server.yaml @@ -13,6 +13,7 @@ source: project: https://github.com/slimslenderslacks/mcp-send-email upstream: https://github.com/resend/mcp-send-email branch: slim/docker + commit: ee47c6dc4776491226fd066380d052e24ca34fd4 config: description: Configure sender and reply email addresses secrets: diff --git a/servers/risken/server.yaml b/servers/risken/server.yaml index fd41b3d6..50c4ad4e 100644 --- a/servers/risken/server.yaml +++ b/servers/risken/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/88366693?s=200&v=4 source: project: https://github.com/ca-risken/risken-mcp-server + commit: e1a0ceefa761ccafb20201cd9b213c921f7a3ac2 run: command: - stdio diff --git a/servers/root/server.yaml b/servers/root/server.yaml index 4c7bf130..4390df9f 100644 --- a/servers/root/server.yaml +++ b/servers/root/server.yaml @@ -14,9 +14,10 @@ about: icon: https://raw.githubusercontent.com/rootio-avr/mcp-proxy/refs/heads/main/assets/root-logo.svg source: project: https://github.com/rootio-avr/mcp-proxy + commit: 1d1eb02366e4b47ae9d86ac510929bde4be1f0da config: description: Configure the Root.io MCP proxy connection secrets: - name: root.api_access_token env: API_ACCESS_TOKEN - example: sk_your_access_token \ No newline at end of file + example: sk_your_access_token diff --git a/servers/ros2/server.yaml b/servers/ros2/server.yaml index 10f683a8..1c8c7e2e 100644 --- a/servers/ros2/server.yaml +++ b/servers/ros2/server.yaml @@ -11,4 +11,5 @@ about: icon: https://avatars.githubusercontent.com/u/85994630?v=4 source: project: https://github.com/wise-vision/mcp_server_ros_2 + commit: ce7405d791788f19c6e601d0e7a36cd647ee6c2d diff --git a/servers/rust-mcp-filesystem/server.yaml b/servers/rust-mcp-filesystem/server.yaml new file mode 100644 index 00000000..e0a3fe5c --- /dev/null +++ b/servers/rust-mcp-filesystem/server.yaml @@ -0,0 +1,52 @@ +name: rust-mcp-filesystem +image: mcp/rust-mcp-filesystem +type: server +meta: + category: productivity + tags: + - productivity +about: + title: Blazing-fast, asynchronous MCP server for seamless filesystem operations. + description: |- + The Rust MCP Filesystem is a high-performance, asynchronous, and lightweight Model Context Protocol (MCP) server built in Rust for secure and efficient filesystem operations. Designed with security in mind, it operates in read-only mode by default and restricts clients from updating allowed directories via MCP Roots unless explicitly enabled, ensuring robust protection against unauthorized access. Leveraging asynchronous I/O, it delivers blazingly fast performance with a minimal resource footprint. + Optimized for token efficiency, the Rust MCP Filesystem enables large language models (LLMs) to precisely target searches and edits within specific sections of large files and restrict operations by file size range, making it ideal for efficient file exploration, automation, and system integration. + icon: https://raw.githubusercontent.com/rust-mcp-stack/rust-mcp-filesystem/refs/heads/main/docs/_media/rust-mcp-filesystem.png +source: + project: https://github.com/rust-mcp-stack/rust-mcp-filesystem + commit: b3c000a03dd8d70795335c291cb46866f5f42fd3 +run: + command: + - "{{rust-mcp-filesystem.allowed_directories|volume-target|into}}" + volumes: + - "{{rust-mcp-filesystem.allowed_directories|volume|into}}" + disableNetwork: true +config: + description: Configure rust-mcp-filesystem to allow access only to permitted directories. + env: + - name: ENABLE_ROOTS + example: "false" + value: "{{rust-mcp-filesystem.enable_roots}}" + - name: ALLOW_WRITE + example: "false" + value: "{{rust-mcp-filesystem.allow_write}}" + parameters: + type: object + properties: + allow_write: + type: boolean + title: Allow Write + description: Enable read/write mode. If false, the app operates in read-only mode. + default: false + enable_roots: + type: boolean + title: Enable MCP Roots + description: Enable dynamic directory access control via MCP client-side Roots. + default: false + allowed_directories: + type: array + items: + type: string + title: Allowed Directories + description: List of directories that rust-mcp-filesystem can access. + default: + - /Users/local-test diff --git a/servers/schemacrawler-ai/server.yaml b/servers/schemacrawler-ai/server.yaml index 1a591280..6214dc6e 100644 --- a/servers/schemacrawler-ai/server.yaml +++ b/servers/schemacrawler-ai/server.yaml @@ -22,6 +22,7 @@ about: source: project: 'https://github.com/schemacrawler/SchemaCrawler-AI' branch: docker-mcp-registry + commit: 63ed6405a0fd640c93d30be8cd463f18e66f1868 run: volumes: - '{{schemacrawler-ai.volume.host_share}}:/share' @@ -93,6 +94,8 @@ config: type: string description: '--info-level How much database metadata to retrieve' default: standard + log_level: + type: string required: - info_level volume: diff --git a/servers/schogini-mcp-image-border/server.yaml b/servers/schogini-mcp-image-border/server.yaml index 480af232..02053c99 100644 --- a/servers/schogini-mcp-image-border/server.yaml +++ b/servers/schogini-mcp-image-border/server.yaml @@ -11,3 +11,4 @@ about: icon: https://d1yei2z3i6k35z.cloudfront.net/7429984/669a56c4ca69e_SCHOGINI-LOGO-ROUND.png source: project: https://github.com/schogini/schogini_mcp_image_border + commit: fc8aa52cec0f5ac9aeff2e57c16ffecd21bb12f7 diff --git a/servers/scorecard/server.yaml b/servers/scorecard/server.yaml index a3b1a3b0..886974e9 100644 --- a/servers/scorecard/server.yaml +++ b/servers/scorecard/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Scorecard - description: AI Evaluation + description: Test and evaluate LLM applications with structured testing, continuous monitoring, and performance benchmarking icon: https://www.google.com/s2/favicons?domain=workers.dev&sz=64 remote: transport_type: sse diff --git a/servers/scrapegraph/server.yaml b/servers/scrapegraph/server.yaml index 2aae8203..e6a9eb8e 100644 --- a/servers/scrapegraph/server.yaml +++ b/servers/scrapegraph/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/171017415?s=200&v=4 source: project: https://github.com/ScrapeGraphAI/scrapegraph-mcp + commit: aebeebd2748311e3ff68d5c10d37b0a903e6206a config: secrets: - name: scrapegraph.api_key diff --git a/servers/scrapezy/server.yaml b/servers/scrapezy/server.yaml index a5bdb41e..3264ddcc 100644 --- a/servers/scrapezy/server.yaml +++ b/servers/scrapezy/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/196929482?s=200&v=4 source: project: https://github.com/Scrapezy/mcp + commit: a8d31506d1f962c384840773e1918a310ec17032 config: secrets: - name: scrapezy.auth_token diff --git a/servers/sec-edgar/server.yaml b/servers/sec-edgar/server.yaml deleted file mode 100644 index c64ee999..00000000 --- a/servers/sec-edgar/server.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: sec-edgar -image: mcp/sec-edgar -type: server -meta: - category: finance - tags: - - sec - - edgar - - finance - - data -about: - title: SEC EDGAR - description: MCP server to interact with the US SEC EDGAR database, developed by the community and maintained by Stefano Amorelli. - icon: https://raw.githubusercontent.com/stefanoamorelli/sec-edgar-mcp/refs/heads/main/sec-edgar-mcp-logo.png -source: - project: https://github.com/stefanoamorelli/sec-edgar-mcp -config: - secrets: - - name: sec_edgar_user_agent - env: SEC_EDGAR_USER_AGENT - example: "Your Name (your.email@domain.com)" diff --git a/servers/securenote-link-mcp-server/server.yaml b/servers/securenote-link-mcp-server/server.yaml index ea85d0bc..7986059d 100644 --- a/servers/securenote-link-mcp-server/server.yaml +++ b/servers/securenote-link-mcp-server/server.yaml @@ -11,4 +11,5 @@ about: icon: https://avatars.githubusercontent.com/u/5723207?v=5 source: project: https://github.com/jackalterman/securenote-link-MCP-server + commit: a184102de77ebae58533e45b56764d4a5fab0a8a diff --git a/servers/sentry-remote/server.yaml b/servers/sentry-remote/server.yaml index 0c984221..3820d593 100644 --- a/servers/sentry-remote/server.yaml +++ b/servers/sentry-remote/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Sentry - description: Software Development + description: Track application errors, monitor performance, and debug issues in real-time across your entire development stack icon: https://www.google.com/s2/favicons?domain=sentry.io&sz=64 remote: transport_type: sse diff --git a/servers/sentry/server.yaml b/servers/sentry/server.yaml index 24cb59ab..5293359a 100644 --- a/servers/sentry/server.yaml +++ b/servers/sentry/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 directory: src/sentry run: command: diff --git a/servers/sequa/server.yaml b/servers/sequa/server.yaml index 4b1a022e..e31a4b0f 100644 --- a/servers/sequa/server.yaml +++ b/servers/sequa/server.yaml @@ -14,6 +14,7 @@ about: source: project: https://github.com/sequa-ai/sequa-mcp branch: main + commit: e569815854166db5f71c2e722408f8957fb9e804 config: description: Configure the connection to Sequa.AI secrets: diff --git a/servers/sequentialthinking/server.yaml b/servers/sequentialthinking/server.yaml index 808654a6..06113138 100644 --- a/servers/sequentialthinking/server.yaml +++ b/servers/sequentialthinking/server.yaml @@ -13,4 +13,5 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.6 + commit: 82064568802e542c3924560aef2cb421b4ce436c directory: src/sequentialthinking diff --git a/servers/simplechecklist/server.yaml b/servers/simplechecklist/server.yaml index 71f040da..ec38efbb 100644 --- a/servers/simplechecklist/server.yaml +++ b/servers/simplechecklist/server.yaml @@ -29,6 +29,7 @@ about: icon: https://raw.githubusercontent.com/DevMayur/SimpleCheckList/main/icon.png source: project: https://github.com/DevMayur/SimpleCheckList + commit: 176694570a11e21304bef0dee8e6345e654022e8 run: command: - --mode=backend diff --git a/servers/simplescraper/readme.md b/servers/simplescraper/readme.md deleted file mode 100644 index 4b671e17..00000000 --- a/servers/simplescraper/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://simplescraper.io/docs/mcp-server diff --git a/servers/simplescraper/server.yaml b/servers/simplescraper/server.yaml deleted file mode 100644 index 34e57cf0..00000000 --- a/servers/simplescraper/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: simplescraper -type: remote -dynamic: - tools: true -meta: - category: automation - tags: - - automation - - web-scraping - - data-extraction - - remote -about: - title: Simplescraper - description: Web Scraping - icon: https://www.google.com/s2/favicons?domain=simplescraper.io&sz=64 -remote: - transport_type: streamable-http - url: https://mcp.simplescraper.io/mcp -oauth: - - provider: simplescraper - secret: simplescraper.personal_access_token - env: SIMPLESCRAPER_PERSONAL_ACCESS_TOKEN diff --git a/servers/simplescraper/tools.json b/servers/simplescraper/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/simplescraper/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/singlestore/server.yaml b/servers/singlestore/server.yaml index c0bf74c6..22971fd3 100644 --- a/servers/singlestore/server.yaml +++ b/servers/singlestore/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/79943160?v=4 source: project: https://github.com/singlestore-labs/mcp-server-singlestore + commit: 1fd9c0c391e2b5c56d330a1578ab2897f550c38e config: secrets: - name: singlestore.mcp_api_key diff --git a/servers/slack/server.yaml b/servers/slack/server.yaml index fab9bb14..d98cc2ac 100644 --- a/servers/slack/server.yaml +++ b/servers/slack/server.yaml @@ -13,6 +13,7 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 dockerfile: src/slack/Dockerfile config: description: Configure the connection to Slack diff --git a/servers/smartbear/server.yaml b/servers/smartbear/server.yaml index 31789a62..f3e28608 100644 --- a/servers/smartbear/server.yaml +++ b/servers/smartbear/server.yaml @@ -10,19 +10,18 @@ meta: - monitoring about: title: SmartBear MCP Server - description: SmartBear's official MCP Server for API Hub, Test Hub and Insight Hub + description: MCP server for AI access to SmartBear tools, including BugSnag, Reflect, API Hub, PactFlow. icon: https://avatars.githubusercontent.com/u/1644671?s=200&v=4 source: project: https://github.com/SmartBear/smartbear-mcp branch: main + commit: bb93963147b01f83e583fe356d627135c6dc39e7 config: description: Configure the connection to SmartBear services secrets: - - name: smartbear.insight_hub_auth_token - env: INSIGHT_HUB_AUTH_TOKEN - required: false - - name: smartbear.insight_hub_project_api_key - env: INSIGHT_HUB_PROJECT_API_KEY + - name: smartbear.bugsnag_auth_token + env: BUGSNAG_AUTH_TOKEN + example: "" required: false - name: smartbear.reflect_api_token example: "" @@ -32,12 +31,35 @@ config: example: "" env: API_HUB_API_KEY required: false + - name: smartbear.pact_broker_token + example: "" + env: PACT_BROKER_TOKEN + required: false + - name: smartbear.pact_broker_password + example: "" + env: PACT_BROKER_PASSWORD + required: false env: - - name: MCP_SERVER_INSIGHT_HUB_API_KEY - example: "00000000000000000000000000000000" - value: '{{smartbear.mcp_server_insight_hub_api_key}}' + - name: BUGSNAG_PROJECT_API_KEY + example: "" + value: "{{smartbear.bugsnag_api_key}}" + - name: BUGSNAG_ENDPOINT + example: "https://notify.your-server.com/" + value: "{{smartbear.bugsnag_endpoint}}" + - name: PACT_BROKER_BASE_URL + example: "https://your-tenant.pactflow.io/" + value: "{{smartbear.pact_broker_base_url}}" + - name: PACT_BROKER_USERNAME + example: "" + value: "{{smartbear.pact_broker_username}}" parameters: type: object properties: - mcp_server_insight_hub_api_key: + bugsnag_api_key: + type: string + bugsnag_endpoint: + type: string + pact_broker_base_url: + type: string + pact_broker_username: type: string diff --git a/servers/sonarqube/server.yaml b/servers/sonarqube/server.yaml index be01a4c6..3a8f9836 100644 --- a/servers/sonarqube/server.yaml +++ b/servers/sonarqube/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/SonarSource/sonarqube-mcp-server branch: master + commit: 6f1cf9eebf593c01844e5fc3f04ac2de49233ab3 config: description: Configure the connection to SonarQube secrets: diff --git a/servers/sqlite-mcp-server/server.yaml b/servers/sqlite-mcp-server/server.yaml index b048247f..51529d0a 100644 --- a/servers/sqlite-mcp-server/server.yaml +++ b/servers/sqlite-mcp-server/server.yaml @@ -130,3 +130,4 @@ about: icon: https://adamic.tech/assets/images/logo.web source: project: https://github.com/neverinfamous/sqlite-mcp-server + commit: bd3d25248d36685fc049e090d28458cabf6ad617 diff --git a/servers/square/readme.md b/servers/square/readme.md deleted file mode 100644 index e774e61b..00000000 --- a/servers/square/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://developer.squareup.com/docs/mcp diff --git a/servers/square/server.yaml b/servers/square/server.yaml deleted file mode 100644 index 4427819a..00000000 --- a/servers/square/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: square -type: remote -dynamic: - tools: true -meta: - category: payments - tags: - - payments - - pos - - commerce - - remote -about: - title: Square - description: Payments - icon: https://www.google.com/s2/favicons?domain=squareup.com&sz=64 -remote: - transport_type: sse - url: https://mcp.squareup.com/sse -oauth: - - provider: square - secret: square.personal_access_token - env: SQUARE_PERSONAL_ACCESS_TOKEN diff --git a/servers/square/tools.json b/servers/square/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/square/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/stackgen/server.yaml b/servers/stackgen/server.yaml index 71473181..5bd6258a 100644 --- a/servers/stackgen/server.yaml +++ b/servers/stackgen/server.yaml @@ -15,6 +15,7 @@ run: - mcp source: project: https://github.com/stackgenhq/homebrew-stackgen + commit: a325c8f53471cfa44546de04487c6ec009de16b7 dockerfile: stackgen/Dockerfile config: description: Configure the connection to StackGen server diff --git a/servers/stackhawk/server.yaml b/servers/stackhawk/server.yaml index d78605fc..13abd0b1 100644 --- a/servers/stackhawk/server.yaml +++ b/servers/stackhawk/server.yaml @@ -14,6 +14,7 @@ about: source: project: https://github.com/stackhawk/stackhawk-mcp branch: feature/image-entrypoint + commit: a330ddfc85aa880346b4555cb168c6ef28562ad3 config: description: Configure the connection to StackHawk secrets: diff --git a/servers/stripe-remote/server.yaml b/servers/stripe-remote/server.yaml index a33f0b50..bd20e127 100644 --- a/servers/stripe-remote/server.yaml +++ b/servers/stripe-remote/server.yaml @@ -14,8 +14,8 @@ about: description: Interact with Stripe services over the Stripe API. icon: https://www.google.com/s2/favicons?domain=stripe.com&sz=64 remote: - transport_type: sse - url: https://mcp.stripe.com/sse + transport_type: streamable-http + url: https://mcp.stripe.com oauth: - provider: stripe-remote secret: stripe-remote.personal_access_token diff --git a/servers/stripe/server.yaml b/servers/stripe/server.yaml index 512c883e..c58a56a7 100644 --- a/servers/stripe/server.yaml +++ b/servers/stripe/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/856813?s=200&v=4 source: project: https://github.com/stripe/agent-toolkit + commit: f495421c400748b65a05751806cb20293c764233 directory: modelcontextprotocol run: command: diff --git a/servers/stytch/readme.md b/servers/stytch/readme.md deleted file mode 100644 index 816967be..00000000 --- a/servers/stytch/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://stytch.com/docs/workspace-management/stytch-mcp diff --git a/servers/stytch/server.yaml b/servers/stytch/server.yaml deleted file mode 100644 index bc104226..00000000 --- a/servers/stytch/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: stytch -type: remote -dynamic: - tools: true -meta: - category: security - tags: - - security - - authentication - - identity - - remote -about: - title: Stytch - description: Authentication - icon: https://www.google.com/s2/favicons?domain=stytch.com&sz=64 -remote: - transport_type: streamable-http - url: http://mcp.stytch.dev/mcp -oauth: - - provider: stytch - secret: stytch.personal_access_token - env: STYTCH_PERSONAL_ACCESS_TOKEN diff --git a/servers/stytch/tools.json b/servers/stytch/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/stytch/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/supadata/server.yaml b/servers/supadata/server.yaml index 347c29ba..2daeee9b 100644 --- a/servers/supadata/server.yaml +++ b/servers/supadata/server.yaml @@ -16,6 +16,7 @@ about: icon: https://avatars.githubusercontent.com/u/194430777?s=48&v=4 source: project: https://github.com/supadata-ai/mcp + commit: 373484ea837e6022151116499618674b761865ea config: description: Configure the connection to Supadata secrets: diff --git a/servers/suzieq/server.yaml b/servers/suzieq/server.yaml index e4aab155..53c30e69 100644 --- a/servers/suzieq/server.yaml +++ b/servers/suzieq/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/182288589?s=200&v=4 source: project: https://github.com/PovedaAqui/suzieq-mcp + commit: 42e74a941237099a36761840d2cc8b5f30a03a5c config: description: Configure the connection to SuzieQ secrets: @@ -21,3 +22,9 @@ config: - name: SUZIEQ_API_ENDPOINT example: none value: '{{suzieq.api_endpoint}}' + parameters: + type: object + properties: + api_endpoint: + type: string + diff --git a/servers/task-orchestrator/server.yaml b/servers/task-orchestrator/server.yaml index 2ff252e1..fb3d0314 100644 --- a/servers/task-orchestrator/server.yaml +++ b/servers/task-orchestrator/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/5747775?v=4 source: project: https://github.com/jpicklyk/task-orchestrator + commit: 919f3762d13c37543a3e604d658f0e8aced8f3e8 run: volumes: - mcp-task-data:/app/data diff --git a/servers/tavily/server.yaml b/servers/tavily/server.yaml index 652fec18..a712c5ef 100644 --- a/servers/tavily/server.yaml +++ b/servers/tavily/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/170207473?s=200&v=4 source: project: https://github.com/tavily-ai/tavily-mcp + commit: fea64be37d3a9589547d8c66af4fb8505fd2b265 config: secrets: - name: tavily.api_token diff --git a/servers/teamwork/server.yaml b/servers/teamwork/server.yaml index 54b4f22c..e938026d 100644 --- a/servers/teamwork/server.yaml +++ b/servers/teamwork/server.yaml @@ -14,6 +14,7 @@ about: source: project: https://github.com/teamwork/mcp branch: main + commit: 8707ecf9887c0cc45b232d3172a42f18a00ce41c config: description: Configure the connection to Teamwork.com MCP server secrets: diff --git a/servers/tembo/server.yaml b/servers/tembo/server.yaml index 8946a532..fa131cc2 100644 --- a/servers/tembo/server.yaml +++ b/servers/tembo/server.yaml @@ -11,6 +11,7 @@ about: icon: https://avatars.githubusercontent.com/u/113815875?s=200&v=4 source: project: https://github.com/tembo-io/mcp-server-tembo + commit: 5fd392db6e5af17e77fdc1a5a9f7d64177ff5166 config: secrets: - name: tembo.api_token diff --git a/servers/terraform/server.yaml b/servers/terraform/server.yaml index 29c51e3b..bbd1eb60 100644 --- a/servers/terraform/server.yaml +++ b/servers/terraform/server.yaml @@ -11,3 +11,4 @@ about: icon: https://avatars.githubusercontent.com/u/761456?v=4 source: project: https://github.com/hashicorp/terraform-mcp-server + commit: 3aa58e49d7e0502f28b25fe3ab12dbea9f76c115 diff --git a/servers/text-to-graphql/server.yaml b/servers/text-to-graphql/server.yaml index b89b401b..38dcc82f 100644 --- a/servers/text-to-graphql/server.yaml +++ b/servers/text-to-graphql/server.yaml @@ -16,6 +16,7 @@ about: source: project: https://github.com/Arize-ai/text-to-graphql-mcp branch: main + commit: 03a88ab0d9d99c3da110e12326803497b094fc56 dockerfile: Dockerfile config: description: Configure API keys and model settings for text-to-graphql @@ -47,4 +48,4 @@ config: model_temperature: type: number default: 0 - description: Model temperature for responses \ No newline at end of file + description: Model temperature for responses diff --git a/servers/thingsboard/server.yaml b/servers/thingsboard/server.yaml index 57e720ac..704db9c2 100644 --- a/servers/thingsboard/server.yaml +++ b/servers/thingsboard/server.yaml @@ -21,6 +21,7 @@ about: icon: https://avatars.githubusercontent.com/u/24291394?v=4 source: project: https://github.com/thingsboard/thingsboard-mcp + commit: adac2d247c408487ef1f99ebb65f8889792dc632 config: description: Configure the connection to ThingsBoard secrets: diff --git a/servers/tigris/server.yaml b/servers/tigris/server.yaml index 16b67150..cf2094a4 100644 --- a/servers/tigris/server.yaml +++ b/servers/tigris/server.yaml @@ -14,6 +14,7 @@ about: icon: https://avatars.githubusercontent.com/u/95787860?v=4 source: project: https://github.com/tigrisdata/tigris-mcp-server + commit: 7d61d54f7a890aaafd8c9795b2e771d8cdcd87f7 config: description: Configure the connection to Tigris secrets: diff --git a/servers/time/server.yaml b/servers/time/server.yaml index 79306f7f..3500547b 100644 --- a/servers/time/server.yaml +++ b/servers/time/server.yaml @@ -13,4 +13,5 @@ about: source: project: https://github.com/modelcontextprotocol/servers branch: 2025.4.24 + commit: b4ee623039a6c60053ce67269701ad9e95073306 directory: src/time diff --git a/servers/triplewhale/server.yaml b/servers/triplewhale/server.yaml index 45791510..cb6efeb3 100644 --- a/servers/triplewhale/server.yaml +++ b/servers/triplewhale/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/Triple-Whale/mcp-server-triplewhale branch: master + commit: 6948082d5eb68f6855aafb279e196ede54e4eaed config: secrets: - name: triplewhale.api_key diff --git a/servers/turkish-airlines/readme.md b/servers/turkish-airlines/readme.md deleted file mode 100644 index 9d52f5b6..00000000 --- a/servers/turkish-airlines/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://mcp.turkishtechlab.com diff --git a/servers/turkish-airlines/server.yaml b/servers/turkish-airlines/server.yaml deleted file mode 100644 index f62cccbc..00000000 --- a/servers/turkish-airlines/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: turkish-airlines -type: remote -dynamic: - tools: true -meta: - category: travel - tags: - - travel - - airlines - - booking - - remote -about: - title: Turkish Airlines - description: Airlines - icon: https://www.google.com/s2/favicons?domain=turkishtechlab.com&sz=64 -remote: - transport_type: streamable-http - url: https://mcp.turkishtechlab.com/mcp -oauth: - - provider: turkish-airlines - secret: turkish-airlines.personal_access_token - env: TURKISH_AIRLINES_PERSONAL_ACCESS_TOKEN diff --git a/servers/turkish-airlines/tools.json b/servers/turkish-airlines/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/turkish-airlines/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/uberall/server.yaml b/servers/uberall/server.yaml index 5ef98cbd..1a21ffb2 100644 --- a/servers/uberall/server.yaml +++ b/servers/uberall/server.yaml @@ -16,6 +16,7 @@ about: icon: https://uberall.com/media/favicon.svg source: project: https://github.com/uberall/uberall-mcp-server + commit: 2015d2554576678d5bfc4b9dedb8c0a28fb57083 config: description: Configure the connection to Uberall API for managing location presence env: diff --git a/servers/unreal-engine-mcp-server/server.yaml b/servers/unreal-engine-mcp-server/server.yaml index 190f74c7..39843c9c 100644 --- a/servers/unreal-engine-mcp-server/server.yaml +++ b/servers/unreal-engine-mcp-server/server.yaml @@ -15,6 +15,7 @@ about: icon: https://raw.githubusercontent.com/ChiR24/Unreal_mcp/main/Public/icon.png source: project: https://github.com/ChiR24/Unreal_mcp + commit: b501784e72cdcba27e3fa2e381df309dedc8f439 config: description: Configure the connection to Unreal Engine Remote Control API env: diff --git a/servers/vercel/readme.md b/servers/vercel/readme.md deleted file mode 100644 index f84823a4..00000000 --- a/servers/vercel/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://vercel.com/docs/mcp/vercel-mcp diff --git a/servers/vercel/server.yaml b/servers/vercel/server.yaml deleted file mode 100644 index a2988900..00000000 --- a/servers/vercel/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: vercel -type: remote -dynamic: - tools: true -meta: - category: devops - tags: - - devops - - hosting - - deployment - - remote -about: - title: Vercel - description: Software Development - icon: https://www.google.com/s2/favicons?domain=vercel.com&sz=64 -remote: - transport_type: streamable-http - url: https://mcp.vercel.com/ -oauth: - - provider: vercel - secret: vercel.personal_access_token - env: VERCEL_PERSONAL_ACCESS_TOKEN diff --git a/servers/vercel/tools.json b/servers/vercel/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/vercel/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/veyrax/server.yaml b/servers/veyrax/server.yaml index a1bc09c3..0e7e3c49 100644 --- a/servers/veyrax/server.yaml +++ b/servers/veyrax/server.yaml @@ -12,6 +12,7 @@ about: icon: https://avatars.githubusercontent.com/u/201344226?s=200&v=4 source: project: https://github.com/VeyraX/veyrax-mcp + commit: 4171ea88cc3a0e28599b68166eb54281470d622f config: secrets: - name: veyrax.api_key diff --git a/servers/vizro/server.yaml b/servers/vizro/server.yaml index f7db6c08..d1ab4cf2 100644 --- a/servers/vizro/server.yaml +++ b/servers/vizro/server.yaml @@ -17,4 +17,5 @@ about: icon: https://avatars.githubusercontent.com/u/4265350?v=4 source: project: https://github.com/mckinsey/vizro + commit: 1a46d5a69f3e8d2a02c2356bc5e633cf8f2b070b directory: vizro-mcp diff --git a/servers/vuln-nist-mcp-server/server.yaml b/servers/vuln-nist-mcp-server/server.yaml index b3d1ad6e..0e816c49 100644 --- a/servers/vuln-nist-mcp-server/server.yaml +++ b/servers/vuln-nist-mcp-server/server.yaml @@ -14,3 +14,4 @@ about: icon: https://raw.githubusercontent.com/HaroldFinchIFT/vuln-nist-mcp-server/refs/heads/main/nvd_logo.png source: project: https://github.com/HaroldFinchIFT/vuln-nist-mcp-server + commit: 71a45579c7dfcbdd0e639dfb6640801371bf07b6 diff --git a/servers/waystation/server.yaml b/servers/waystation/server.yaml index 95ef56ef..1761afc4 100644 --- a/servers/waystation/server.yaml +++ b/servers/waystation/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: WayStation - description: Productivity + description: Connect AI to your productivity ecosystem with integrations across Notion, Slack, Asana, Monday.com, and 50+ other business tools icon: https://www.google.com/s2/favicons?domain=waystation.ai&sz=64 remote: transport_type: streamable-http diff --git a/servers/webflow-remote/server.yaml b/servers/webflow-remote/server.yaml index e96d029c..62ac0194 100644 --- a/servers/webflow-remote/server.yaml +++ b/servers/webflow-remote/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Webflow - description: CMS + description: Design and build professional websites visually with no-code tools, advanced CMS, and responsive layouts icon: https://www.google.com/s2/favicons?domain=webflow.com&sz=64 remote: transport_type: sse diff --git a/servers/webflow/server.yaml b/servers/webflow/server.yaml index 7374481c..71abfeb4 100644 --- a/servers/webflow/server.yaml +++ b/servers/webflow/server.yaml @@ -13,6 +13,7 @@ source: project: https://github.com/slimslenderslacks/mcp-server upstream: https://github.com/webflow/mcp-server branch: slim/docker + commit: ed02aacd106375cfca402b73f8b7efffd4168c1d config: secrets: - name: webflow.token diff --git a/servers/wikipedia-mcp/server.yaml b/servers/wikipedia-mcp/server.yaml index 90c6cf99..6ae26462 100644 --- a/servers/wikipedia-mcp/server.yaml +++ b/servers/wikipedia-mcp/server.yaml @@ -11,3 +11,4 @@ about: icon: https://avatars.githubusercontent.com/u/56668?s=200&v=4 source: project: https://github.com/Rudra-ravi/wikipedia-mcp + commit: 5899451ddd18c8df354eb6727c096d147d1bd30a diff --git a/servers/wix/server.yaml b/servers/wix/server.yaml index 49c6498b..d814d495 100644 --- a/servers/wix/server.yaml +++ b/servers/wix/server.yaml @@ -11,7 +11,7 @@ meta: - remote about: title: Wix - description: CMS + description: Build and manage websites with drag-and-drop tools, content management, and dynamic pages icon: https://www.google.com/s2/favicons?domain=wix.com&sz=64 remote: transport_type: sse diff --git a/servers/wolfram-alpha/server.yaml b/servers/wolfram-alpha/server.yaml index 66d1dc56..638ec816 100644 --- a/servers/wolfram-alpha/server.yaml +++ b/servers/wolfram-alpha/server.yaml @@ -12,6 +12,7 @@ about: source: project: https://github.com/SecretiveShell/MCP-wolfram-alpha branch: master + commit: 0aa2530c8da8f3976a8c5383f9f9e1376bd337df config: secrets: - name: wolfram-alpha.api_key diff --git a/servers/youtube_transcript/server.yaml b/servers/youtube_transcript/server.yaml index 2b919348..45b79a15 100644 --- a/servers/youtube_transcript/server.yaml +++ b/servers/youtube_transcript/server.yaml @@ -12,3 +12,4 @@ about: icon: https://avatars.githubusercontent.com/u/4052902?s=200&v=4 source: project: https://github.com/jkawamoto/mcp-youtube-transcript + commit: c2219ad0a4cc111a3ae07e543eb6538efa5606ee diff --git a/servers/zapier/readme.md b/servers/zapier/readme.md deleted file mode 100644 index ad90a97b..00000000 --- a/servers/zapier/readme.md +++ /dev/null @@ -1 +0,0 @@ -Docs: https://zapier.com/blog/zapier-mcp-guide/ diff --git a/servers/zapier/server.yaml b/servers/zapier/server.yaml deleted file mode 100644 index e17ab25b..00000000 --- a/servers/zapier/server.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: zapier -type: remote -dynamic: - tools: true -meta: - category: automation - tags: - - automation - - integration - - workflow - - remote -about: - title: Zapier - description: Automation - icon: https://www.google.com/s2/favicons?domain=zapier.com&sz=64 -remote: - transport_type: sse - url: https://mcp.zapier.com/sse -oauth: - - provider: zapier - secret: zapier.personal_access_token - env: ZAPIER_PERSONAL_ACCESS_TOKEN diff --git a/servers/zapier/tools.json b/servers/zapier/tools.json deleted file mode 100644 index fe51488c..00000000 --- a/servers/zapier/tools.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/servers/zerodha-kite/server.yaml b/servers/zerodha-kite/server.yaml index 6fcf0b33..48699e08 100644 --- a/servers/zerodha-kite/server.yaml +++ b/servers/zerodha-kite/server.yaml @@ -14,6 +14,7 @@ about: icon: https://avatars.githubusercontent.com/u/34680622?s=300&v=4 source: project: https://github.com/anshuljain90/zerodha-kite-mcp + commit: 3902c5283c2c98b567e1de35c86d0ca9fe0c3403 config: description: Configure your Zerodha Kite Connect API credentials. Requires an active Zerodha trading account and Kite Connect developer app. secrets: @@ -43,4 +44,4 @@ config: type: string description: OAuth redirect URL configured in your Kite Connect app required: - - kite_api_key \ No newline at end of file + - kite_api_key