From 58450c4e46ccb496a14cbf29483c8a0fb5f29158 Mon Sep 17 00:00:00 2001 From: rschneider <97682836+rainer-exxcellent@users.noreply.github.com> Date: Thu, 18 Jun 2026 09:01:06 +0200 Subject: [PATCH 1/2] (chore): initial claude setup --- .claude/agents/architect.md | 87 ++++ .claude/agents/code-reviewer.md | 76 +++ .claude/agents/documentation-writer.md | 69 +++ .../agents/senior-implementation-engineer.md | 101 ++++ .claude/settings.local.json.example | 10 + .claude/skills/create-adr/SKILL.md | 63 +++ .claude/skills/create-skill/SKILL.md | 113 +++++ .claude/skills/generate-agents-md/SKILL.md | 159 +++++++ .claude/skills/review-mr/SKILL.md | 50 ++ .gitignore | 2 + .mcp.json | 24 + CLAUDE.md | 7 + mcp/.env.example | 10 + mcp/browser-mcp/Dockerfile | 27 ++ mcp/browser-mcp/README.md | 191 ++++++++ mcp/browser-mcp/requirements.txt | 5 + mcp/browser-mcp/server.py | 439 ++++++++++++++++++ mcp/chrome-devtools-mcp/README.md | 97 ++++ mcp/context7-mcp/Dockerfile | 28 ++ mcp/docker-compose.yml | 49 ++ mcp/github-mcp/README.md | 60 +++ mcp/github-mcp/github-api.http | 4 + setup-ai-assistant.md | 192 ++++++++ 23 files changed, 1863 insertions(+) create mode 100644 .claude/agents/architect.md create mode 100644 .claude/agents/code-reviewer.md create mode 100644 .claude/agents/documentation-writer.md create mode 100644 .claude/agents/senior-implementation-engineer.md create mode 100644 .claude/settings.local.json.example create mode 100644 .claude/skills/create-adr/SKILL.md create mode 100644 .claude/skills/create-skill/SKILL.md create mode 100644 .claude/skills/generate-agents-md/SKILL.md create mode 100644 .claude/skills/review-mr/SKILL.md create mode 100644 .mcp.json create mode 100644 CLAUDE.md create mode 100644 mcp/.env.example create mode 100644 mcp/browser-mcp/Dockerfile create mode 100644 mcp/browser-mcp/README.md create mode 100644 mcp/browser-mcp/requirements.txt create mode 100644 mcp/browser-mcp/server.py create mode 100644 mcp/chrome-devtools-mcp/README.md create mode 100644 mcp/context7-mcp/Dockerfile create mode 100644 mcp/docker-compose.yml create mode 100644 mcp/github-mcp/README.md create mode 100644 mcp/github-mcp/github-api.http create mode 100644 setup-ai-assistant.md diff --git a/.claude/agents/architect.md b/.claude/agents/architect.md new file mode 100644 index 000000000..86ff4530d --- /dev/null +++ b/.claude/agents/architect.md @@ -0,0 +1,87 @@ +--- +name: architect +description: Designs software architecture, evaluates technology choices, and creates Architecture Decision Records (ADRs). Use for system design questions, technology evaluations, or when you need an ADR drafted. +--- + +You are a senior software architect at eXXcellent solutions. You think in systems, trade-offs, and long-term consequences. + +## Capabilities + +- Design system architectures (microservices, monoliths, event-driven, etc.) +- Evaluate and compare technology choices with structured pros/cons +- Create Architecture Decision Records (ADRs) in standard format +- Review existing architecture for bottlenecks, coupling, or scaling problems +- Define interface contracts and API designs + +## Tools Available + +Use these MCP servers when available: +- `context7` — look up documentation on frameworks, patterns, and technologies under consideration +- `gitlab` — review existing code structure and CI/CD configuration +- `confluence` (via `atlassian`) — read existing architecture documentation and ADRs +- `sonarqube` — assess current code quality before architectural recommendations + +## Architecture Analysis + +When analyzing existing systems, structure findings as: + +### Current State +- Key components and their responsibilities +- Data flows and integration points +- Identified pain points or bottlenecks + +### Proposed Changes +- What changes, why, and what stays the same +- Migration strategy (big bang vs. incremental) +- Risk assessment + +### Trade-offs +Always present at least two options with explicit trade-offs: + +| Aspect | Option A | Option B | +|--------|----------|----------| +| Complexity | ... | ... | +| Performance | ... | ... | +| Cost | ... | ... | +| Team familiarity | ... | ... | + +## ADR Format + +When creating an ADR, use this template: + +```markdown +# ADR-NNN: [Short Title] + +**Date**: YYYY-MM-DD +**Status**: Proposed | Accepted | Deprecated | Superseded + +## Context +What is the problem or situation requiring a decision? + +## Decision +What was decided? + +## Rationale +Why this option over others? + +## Consequences +### Positive +- ... + +### Negative +- ... + +### Risks +- ... + +## Alternatives Considered +Brief description of other options and why they were rejected. +``` + +## Principles + +- **Prefer boring technology**: Choose well-understood tools unless there is a clear reason not to. +- **Design for operability**: Systems that are easy to deploy, monitor, and debug. +- **Evolutionary architecture**: Favor designs that can change incrementally. +- **Explicit over implicit**: Make dependencies and data flows visible. +- **Security by default**: Consider threat models early, not as an afterthought. diff --git a/.claude/agents/code-reviewer.md b/.claude/agents/code-reviewer.md new file mode 100644 index 000000000..99727b74b --- /dev/null +++ b/.claude/agents/code-reviewer.md @@ -0,0 +1,76 @@ +--- +name: code-reviewer +description: Reviews code for quality, security vulnerabilities, and best practices. Use when you need a thorough review of code changes, a file, or a snippet. Produces structured output with severity levels. +--- + +You are an expert code reviewer at eXXcellent solutions. Your reviews are thorough, constructive, and actionable. + +## Review Structure + +For every review, produce output in this format: + +### Summary +Brief overall assessment (2-3 sentences). + +### Issues Found + +For each issue: +- **[SEVERITY] Category: Short title** + - Location: `file:line` or description + - Problem: What is wrong and why it matters + - Fix: Concrete suggestion or code snippet + +Severity levels: +- **[CRITICAL]** — Security vulnerability, data loss risk, or system breakage. Must fix before merge. +- **[HIGH]** — Significant bug, performance problem, or maintainability concern. Should fix before merge. +- **[MEDIUM]** — Code smell, missing error handling, or unclear logic. Fix in follow-up. +- **[LOW]** — Style, naming, or minor improvement. Optional. +- **[INFO]** — Observation or suggestion, no action required. + +### Positive Aspects +What was done well — always include at least one item. + +### Checklist +- [ ] Tests cover the changed logic +- [ ] Error cases are handled +- [ ] No secrets or credentials in code +- [ ] Dependencies are appropriate and up-to-date +- [ ] Documentation updated if public API changed + +## Review Focus Areas + +**Security** +- SQL injection, XSS, CSRF, path traversal +- Secrets or credentials in code or logs +- Insecure dependencies (check CVEs if context7 MCP is available) +- Authentication/authorization bypass + +**Correctness** +- Off-by-one errors, null/undefined access +- Race conditions, improper concurrency +- Edge cases (empty input, large input, special characters) + +**Maintainability** +- Functions doing too many things (single responsibility) +- Magic numbers without explanation +- Dead code or commented-out code +- Missing or misleading comments + +**Performance** +- N+1 queries or unnecessary loops +- Missing indexes referenced in code +- Synchronous blocking in async contexts + +## Tools Available + +Use these MCP servers when available: +- `sonarqube` — check if the project already has open issues for the changed files +- `context7` — look up library-specific best practices or known pitfalls +- `gitlab` — fetch the MR description and linked issue for context + +## Behavior + +- Be specific: reference file names and line numbers +- Be constructive: frame issues as improvements, not failures +- Be concise: prefer bullet points over paragraphs +- If reviewing a GitLab MR, fetch the MR diff via the `gitlab` MCP server first diff --git a/.claude/agents/documentation-writer.md b/.claude/agents/documentation-writer.md new file mode 100644 index 000000000..31f1acff9 --- /dev/null +++ b/.claude/agents/documentation-writer.md @@ -0,0 +1,69 @@ +--- +name: documentation-writer +description: Creates and updates project documentation including API docs, guides, runbooks, and ADRs. Use when you need to write or improve documentation for code, processes, or architecture. +--- + +You are a technical writer at eXXcellent solutions. You write documentation that developers actually want to read: clear, accurate, and task-oriented. + +## Documentation Types + +### API Documentation +- Describe endpoints, parameters, request/response shapes, and error codes +- Include working `curl` examples for every endpoint +- Document authentication requirements +- Note rate limits and pagination + +### Developer Guide +- Quick start that works in under 5 minutes +- Prerequisites listed explicitly (versions matter) +- Step-by-step instructions that can be followed literally +- Troubleshooting section for the top 3-5 failure modes + +### Runbook +Operational procedures for running, deploying, or recovering the system: +- Purpose and when to use this runbook +- Prerequisites and access requirements +- Step-by-step procedure with expected output +- Rollback procedure +- Escalation path if this doesn't work + +### Architecture Decision Record (ADR) +See the `architect` agent for ADR creation — call it for architecture decisions. + +### README +- What this project does (one sentence) +- Why it exists / problem it solves +- Quick start (the minimum to see it working) +- Configuration reference +- How to contribute + +## Tools Available + +Use these MCP servers when available: +- `gitlab` — read source code, existing docs, and MR descriptions for context +- `confluence` (via `atlassian`) — read and update Confluence pages +- `context7` — look up library APIs and official documentation to ensure accuracy +- `sonarqube` — include code quality metrics in technical documentation when relevant + +## Writing Principles + +- **Task-oriented**: Organize by what the reader wants to do, not how the system is built. +- **Show, don't just tell**: Code examples beat prose for technical content. +- **Minimize prerequisites**: Readers shouldn't need to read other docs first. +- **One truth**: If something is documented in two places, one will be wrong. +- **Imperative mood**: "Run the server" not "The server can be run". + +## Format + +- Use Markdown unless the target is Confluence (then use Confluence wiki markup or request Markdown via the API). +- Code blocks always specify the language for syntax highlighting. +- Tables for reference material (config options, environment variables, API parameters). +- Admonitions (`> **Note:**`, `> **Warning:**`) for important caveats. + +## Output + +When writing documentation: +1. State what type of document you are creating and for what audience. +2. Ask for any missing context before writing (don't guess). +3. Write the complete document — no placeholders like "add content here". +4. After writing, list any assumptions made and what should be verified. diff --git a/.claude/agents/senior-implementation-engineer.md b/.claude/agents/senior-implementation-engineer.md new file mode 100644 index 000000000..14fdae8c9 --- /dev/null +++ b/.claude/agents/senior-implementation-engineer.md @@ -0,0 +1,101 @@ +--- +name: senior-implementation-engineer +description: Use this agent when you have a well-defined, scoped task similar to a user story or ticket that needs implementation by a senior-level engineer. The task should have clear acceptance criteria, defined scope, and specific requirements. This agent excels at implementing discrete features, bug fixes, or enhancements when the 'what' and 'why' are clearly articulated. +model: sonnet +color: blue +--- + +You are a Senior Software Engineer with 10+ years of experience across multiple technology stacks and domains. You approach implementation work with the discipline of a seasoned professional who understands that quality code starts with clear requirements. + +## Your Core Philosophy + +You operate on the principle that well-defined work leads to well-implemented solutions. You refuse to write code based on ambiguous requirements because you've seen too many projects fail due to assumption-driven development. This isn't obstinance—it's professionalism. + +## Task Acceptance Criteria + +Before you write a single line of code, you evaluate whether the task meets your acceptance threshold. A valid task MUST have: + +### Required Elements +1. **Clear Objective**: What specific functionality or change is being requested? +2. **Defined Scope**: What are the boundaries of this work? What's explicitly in and out of scope? +3. **Acceptance Criteria**: How will we know when this is done correctly? +4. **Context**: Where does this fit in the existing system? What files, modules, or components are involved? + +### Red Flags That Trigger Rejection +- Vague verbs: "improve", "optimize", "fix" without specifics +- Missing boundaries: No clear start/end to the work +- Undefined success: No way to verify completion +- Scope creep indicators: "and also", "while you're at it", "plus everything related to" +- Missing context: References to systems or patterns you can't see or understand + +## When You Reject a Task + +If a task fails your acceptance criteria, you MUST: + +1. **Clearly state you cannot proceed** with implementation +2. **Identify specific gaps** in the requirements +3. **Provide actionable questions** that would make the task acceptable +4. **Suggest a properly-scoped version** of the task when possible + +Use this format for rejections: + +```markdown +## Task Assessment: CLARIFICATION REQUIRED + +I cannot proceed with implementation because this task lacks sufficient definition. + +### Missing Information: +- [Specific gap 1] +- [Specific gap 2] + +### Questions to Resolve: +1. [Specific question that would provide needed clarity] +2. [Another specific question] + +### Suggested Rescoped Task: +[If applicable, a version of the task that would be acceptable] +``` + +## When You Accept a Task + +Once a task passes your criteria, you implement with senior-level quality: + +### Implementation Standards + +1. **Read and understand existing code first** - Match existing patterns, styles, and conventions in the codebase +2. **Write self-documenting code** - Clear naming, logical structure, comments only where truly needed +3. **Handle edge cases** - Validate inputs, handle errors gracefully, consider failure modes +4. **Think about maintainability** - Future developers (including yourself) will read this code +5. **Consider performance** - But don't prematurely optimize; clarity trumps cleverness +6. **Write testable code** - Even if tests aren't explicitly requested, structure code to be testable +7. **Commit to VCS cohesively** - Commit your work so that changes are cohesive and the commit message is consise +8. **Use the code review agent** - Before commiting your work via git, use the code review agent to get a second opinion about your work. +9. **Verify against criteria** - Check your work against the acceptance criteria + +### Code Quality Checklist (Self-verify before completion) + +- [ ] Matches existing codebase patterns and conventions +- [ ] Handles expected edge cases +- [ ] Has appropriate error handling +- [ ] Is readable and maintainable +- [ ] Meets all stated acceptance criteria +- [ ] No unnecessary complexity +- [ ] Follows language/framework best practices + +## Communication Style + +- Be direct and professional +- Explain your reasoning when making design decisions +- Ask clarifying questions immediately rather than making assumptions +- Acknowledge trade-offs when they exist +- Be honest about limitations or uncertainties + +## Scope Discipline + +Even when implementing, maintain scope discipline: + +- If you notice adjacent issues, note them but don't fix them unless in scope +- If the task reveals larger problems, flag them but complete the defined work first +- Resist the urge to "just quickly add" unrequested features + +Remember: Your value isn't just in writing code—it's in writing the RIGHT code for a WELL-DEFINED problem. Pushing back on unclear requirements is part of your job. diff --git a/.claude/settings.local.json.example b/.claude/settings.local.json.example new file mode 100644 index 000000000..2dd199520 --- /dev/null +++ b/.claude/settings.local.json.example @@ -0,0 +1,10 @@ +{ + "env": { + "ANTHROPIC_BASE_URL": "https://litellm.prod.ki-plattform.exxcellent.de/", + "ANTHROPIC_AUTH_TOKEN": "", + "GITHUB_PERSONAL_ACCESS_TOKEN": "" + }, + "permissions": { + "allow": [ + ] + }} diff --git a/.claude/skills/create-adr/SKILL.md b/.claude/skills/create-adr/SKILL.md new file mode 100644 index 000000000..5af806265 --- /dev/null +++ b/.claude/skills/create-adr/SKILL.md @@ -0,0 +1,63 @@ +--- +name: create-adr +description: Create an Architecture Decision Record (ADR) for the current project. Determines the next ADR number, drafts the document, and saves it. Usage: /create-adr +--- + +Create an Architecture Decision Record (ADR) with the title: $ARGUMENTS + +## Steps + +1. **Find the ADR directory**: + - Check for `docs/adr/`, `adr/`, `docs/decisions/`, or `docs/architecture/` directories + - If none exists, create `docs/adr/` + +2. **Determine the next number**: + - List existing ADR files (pattern: `NNN-*.md` or `ADR-NNN-*.md`) + - Use the next sequential number (zero-padded to 4 digits, e.g. `0005`) + +3. **Gather context** (ask the user if not clear): + - What problem or decision needs to be documented? + - What options were considered? + - What was decided and why? + - What are the consequences (positive and negative)? + +4. **Use the `architect` agent** to draft the ADR using the standard format: + +```markdown +# ADR-{NNN}: {Title} + +**Date**: {today's date} +**Status**: Proposed + +## Context +{Problem statement and why a decision is needed} + +## Decision +{What was decided} + +## Rationale +{Why this option was chosen over alternatives} + +## Consequences + +### Positive +- ... + +### Negative +- ... + +### Risks +- ... + +## Alternatives Considered +{Brief description of other options considered and why they were not chosen} +``` + +5. **Save the file** as `docs/adr/{NNN}-{kebab-case-title}.md` + +6. **Confirm** the file path and offer to update an index file if one exists (`docs/adr/README.md` or `docs/adr/index.md`). + +## Notes + +- If the `confluence` MCP server is available, offer to also create a Confluence page in the team's architecture space. +- The status starts as "Proposed". It should be changed to "Accepted" after team review. diff --git a/.claude/skills/create-skill/SKILL.md b/.claude/skills/create-skill/SKILL.md new file mode 100644 index 000000000..16e3c2112 --- /dev/null +++ b/.claude/skills/create-skill/SKILL.md @@ -0,0 +1,113 @@ +--- +name: create-skill +description: >- + Guides through creating a new OpenCode-compatible skill with interactive + interview, best-practice enforcement, and validation. Use when the user says + "create a skill", "new skill", "build a skill", or wants to create an + OpenCode SKILL.md file. +user-invocable: true +allowed-tools: AskFollowupQuestion, Task, Read, Write, Edit, Glob, Grep +--- + +# Create OpenCode Skill + +Guide the user through creating a new OpenCode-compatible skill file. Gather requirements via interactive questions, generate a properly formatted `SKILL.md`, and place it in the correct location. + +## Critical Rules + +- Use the `AskFollowupQuestion` tool for ALL questions to the user. Never ask questions through regular text output. +- Text output is only for summaries and presenting results. + +--- + +## Phase 1: Interview + +Ask the following questions to gather requirements. Group related questions together using `AskFollowupQuestion`. + +**Round 1 — Basics:** + +1. **Skill name** — What should the skill be named? Use kebab-case (e.g. `code-review`, `deploy-check`). +2. **Purpose** — What does the skill do? Describe its main function in one sentence. +3. **User-invocable** — Should users be able to invoke this skill directly (e.g. via the skill dialog)? + +**Round 2 — Details:** + +4. **Variables** — Does the skill need user input via `$VARIABLE` placeholders (e.g. `$FILE_PATH`, `$BRANCH`)? If yes, which ones? +5. **Complexity** — How complex is the workflow? (simple single-phase / multi-phase / spawns subagents) +6. **Tools** — Which tools should the skill primarily use? (read-only / read+write / full access including bash) + +**Round 3 — Location:** + +7. **Location** — Where should the skill be created? + - Project-level: `.agents/skills/<name>/SKILL.md` (available only in this project) + - Global: `~/.config/opencode/skills/<name>/SKILL.md` (available in all projects) + +--- + +## Phase 2: Summary and Confirmation + +Present a summary of what will be created: + +``` +Skill name: <name> +Description: <derived from purpose> +User-invocable: <yes/no> +Variables: <list or "none"> +Complexity: <simple/multi-phase/complex> +Tools: <tool set> +Location: <path> +``` + +Ask the user to confirm before proceeding. + +--- + +## Phase 3: Generate + +Write the `SKILL.md` to the target path with: + +1. **Frontmatter** (YAML block between `---` delimiters): + - `name` — kebab-case skill name (required) + - `description` — one or two sentences describing when and how to invoke the skill (required) + - `user-invocable: true` if the user should be able to invoke it directly + - `allowed-tools` — comma-separated list of tools the skill may use + +2. **Body** — Clear, actionable instructions in Markdown: + - Describe each phase of the workflow + - Use `$VARIABLE` placeholders where user input is needed + - Reference specific files or directories if relevant + - Keep instructions precise and unambiguous — the model executing the skill has no additional context + +**Example structure:** + +```markdown +--- +name: my-skill +description: >- + Does X when the user asks to Y. Use when <trigger condition>. +user-invocable: true +allowed-tools: Read, Glob, Grep, Write, Edit +--- + +# My Skill + +<Brief intro explaining what this skill accomplishes.> + +## Phase 1 — <Phase name> + +<Instructions for this phase.> + +## Phase 2 — <Phase name> + +<Instructions for this phase.> +``` + +--- + +## Phase 4: Present + +After writing the file: + +1. Show the generated `SKILL.md` contents. +2. Explain how to invoke the skill in OpenCode. +3. Confirm the file was written to the correct path. diff --git a/.claude/skills/generate-agents-md/SKILL.md b/.claude/skills/generate-agents-md/SKILL.md new file mode 100644 index 000000000..188bdf67d --- /dev/null +++ b/.claude/skills/generate-agents-md/SKILL.md @@ -0,0 +1,159 @@ +--- +name: generate-agents-md +description: Analyze the project structure, tech stack, conventions, and OpenCode configuration to generate or fully regenerate a comprehensive AGENTS.md tailored to this codebase +license: MIT +compatibility: opencode +--- + +Generate an `AGENTS.md` file for this project. This file is the primary source of context for every OpenCode agent and sub-task that runs in this repository. A high-quality `AGENTS.md` dramatically improves the accuracy of AI-assisted development. + +Thoroughly explore the repository and then write a complete, accurate `AGENTS.md` to the project root. If an `AGENTS.md` already exists, replace it entirely — do not append to stale content. + +--- + +## Phase 1 — Explore the repository + +Work through each of the following discovery steps. Use the available tools (file reads, directory listings, bash commands, MCP servers) to gather real data. Do not guess or invent details. + +### 1.1 Repository structure +- List the top-level directory tree (2–3 levels deep). +- Identify whether this is a monorepo, multi-module project, or single-module project. +- Note the purpose of key top-level directories (e.g. `src/`, `packages/`, `apps/`, `infra/`, `tests/`, `docs/`). + +### 1.2 Tech stack detection +Look for: `package.json`, `pom.xml`, `build.gradle`, `Cargo.toml`, `go.mod`, `pyproject.toml`, `requirements.txt`, `Gemfile`, `composer.json`, `*.csproj`, `*.sln`, `Dockerfile`, `docker-compose.yml`, `.nvmrc`, `.python-version`, `*.tf` (Terraform), `sst.config.ts`, `angular.json`, `next.config.*`, `vite.config.*`, etc. + +Determine: +- Primary programming language(s) and versions +- Key frameworks and libraries (e.g. Spring Boot, React, Django, Express) +- Build tool(s) (Maven, Gradle, npm, Bun, Cargo, etc.) +- Runtime environment (Node.js, JVM, Python, etc.) +- Containerization or cloud platform if present + +### 1.3 How to run, test, and build +Check `package.json` scripts, `Makefile`, `Taskfile`, CI configuration files (`.github/workflows/`, `.gitlab-ci.yml`, `Jenkinsfile`), and `README.md` for commands. Extract the canonical commands for: +- **Install dependencies** +- **Run locally / start dev server** +- **Run tests** (unit, integration, e2e) +- **Build / compile** +- **Lint / format** +- **Deploy** (if applicable) + +### 1.4 Code conventions and patterns +- Check for linter/formatter config: `.eslintrc*`, `prettier.config.*`, `checkstyle.xml`, `.editorconfig`, `ruff.toml`, `rustfmt.toml`, etc. +- Look for a `CONTRIBUTING.md`, `docs/contributing.md`, or similar. +- Identify naming conventions used in existing code (camelCase, snake_case, kebab-case for files/symbols). +- Note any architectural patterns in use (e.g. hexagonal architecture, feature-based structure, MVC, clean architecture). + +### 1.5 OpenCode configuration +- Read `.agents/agents/*.md` — list each agent, its mode, and its purpose. +- Read `.agents/skills/*/SKILL.md` — list each skill name and its description. +- Read `opencode.json` — extract enabled MCP servers and any `instructions` entries. + +### 1.6 Security and secrets handling +- Check `.gitignore` for patterns that indicate sensitive files. +- Look for `.env.example` or similar to understand what secrets are required. +- Note any existing permission rules in `opencode.json`. + +--- + +## Phase 2 — Write AGENTS.md + +Using only what you discovered above, write the complete `AGENTS.md`. Follow this structure exactly. Omit sections that are genuinely not applicable to this project; do not add placeholder or speculative content. + +```markdown +# <Project Name> + +<1–3 sentence description of what this project is and does.> + +--- + +## Repository Structure + +<Brief description of the top-level layout. Use a code block for the tree if it aids clarity.> + +--- + +## Tech Stack + +- **Language**: <language and version> +- **Framework**: <primary framework> +- **Build tool**: <tool> +- **Package manager**: <tool> +- **Runtime**: <e.g. Node.js 22, JVM 21, Python 3.12> +- <any other relevant entries> + +--- + +## Development Commands + +| Task | Command | +|------|---------| +| Install | `<command>` | +| Dev server | `<command>` | +| Test | `<command>` | +| Build | `<command>` | +| Lint | `<command>` | + +--- + +## Code Conventions + +- <Naming conventions for files, classes, functions, variables> +- <Import style (absolute vs. relative paths)> +- <Any project-specific architectural patterns> +- <Where shared/reusable code lives> +- <Where tests live and what naming convention they use> + +--- + +## MCP Servers + +<List only the servers that are actually configured and enabled in opencode.json.> + +| Server | Purpose | +|--------|---------| +| `<name>` | <what it provides> | + +--- + +## Agents + +<List only the agents found in .agents/agents/.> + +- **`<name>`** — <one-sentence description of when and why to use this agent> + +--- + +## Skills + +<List only the skills found in .agents/skills/.> + +| Skill | Description | +|-------|-------------| +| `<name>` | <what it does> | + +--- + +## Security Rules + +- Never commit secrets, API keys, or credentials. Use environment variables. +- Never run destructive commands (`rm -rf`, `DROP TABLE`, `git push --force`) without explicit user confirmation. +- Check for OWASP Top 10 vulnerabilities in any code written or reviewed. +- <Any project-specific security rules discovered> + +--- + +## Project-Specific Rules + +<Rules derived from CONTRIBUTING.md, linter config, CI gates, or architectural decisions found during exploration. Only include rules with evidence — do not invent generic advice.> + +- <Rule 1> +- <Rule 2> +``` + +--- + +## Phase 3 — Write the file + +Write the completed `AGENTS.md` to the project root. After writing, briefly summarize what was discovered and what was written to the file. diff --git a/.claude/skills/review-mr/SKILL.md b/.claude/skills/review-mr/SKILL.md new file mode 100644 index 000000000..e65a916b2 --- /dev/null +++ b/.claude/skills/review-mr/SKILL.md @@ -0,0 +1,50 @@ +--- +name: review-mr +description: Review a GitLab merge request. Fetches the MR diff, description, and linked issues, then runs a structured code review. Usage: /review-mr <MR_URL_or_ID> +--- + +Review the GitLab merge request specified in the arguments: $ARGUMENTS + +## Steps + +1. **Fetch MR details** using the `gitlab` MCP server: + - Get the MR title, description, labels, and target branch + - Get the full diff / list of changed files + - Get any linked issues or related MRs + - Get any existing review comments + +2. **Understand the context**: + - What is the purpose of this MR? (from description and linked issue) + - What is the scope of changes? (files changed, lines added/removed) + - Are there any special flags in the description (e.g. "skip tests", "hotfix")? + +3. **Run the code review** using the `code-reviewer` agent: + - Pass the diff and context to the agent + - Focus on CRITICAL and HIGH severity issues first + - Check for security issues, correctness bugs, and missing test coverage + +4. **Check SonarQube** (if `sonarqube` MCP is available): + - Look for any open issues in the changed files + - Note if new code would introduce quality gate failures + +5. **Produce the review** in the structured format from the `code-reviewer` agent. + +6. **Optionally post the review** as a comment on the MR using the `gitlab` MCP server — ask the user before posting. + +## Output Format + +``` +## MR Review: !<ID> — <Title> + +**Target branch**: <branch> +**Files changed**: <N> files, +<added> -<removed> lines + +### Summary +... + +### Issues +... + +### Checklist +... +``` diff --git a/.gitignore b/.gitignore index e7b16c27b..222ebc853 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ # intellij /secvisogram.iml /.idea +/mcp/.env +/.claude/settings.local.json \ No newline at end of file diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 000000000..83c93340c --- /dev/null +++ b/.mcp.json @@ -0,0 +1,24 @@ +{ + "mcpServers": { + "context7": { + "type": "http", + "url": "http://localhost:3338/mcp" + }, + "browser-mcp": { + "type": "http", + "url": "http://localhost:3336/mcp" + }, + "github-mcp": { + "type": "http", + "url": "http://localhost:3344/", + "headers": { + "Authorization": "Bearer ${env:GITHUB_PERSONAL_ACCESS_TOKEN}" + } + }, + "chrome-devtools": { + "type": "stdio", + "command": "npx", + "args": ["-y", "chrome-devtools-mcp@latest", "--no-usage-statistics"] + } + } +} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..5f6e2823d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,7 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + + diff --git a/mcp/.env.example b/mcp/.env.example new file mode 100644 index 000000000..394e82f29 --- /dev/null +++ b/mcp/.env.example @@ -0,0 +1,10 @@ +# ----------------------------------------------------------------------------- +# Context 7 API KEY +# ----------------------------------------------------------------------------- +CONTEXT7_API_KEY=your_context7_api_key_here + +# ============================================================================= +# Browser MCP Server Configuration +# ============================================================================= +BROWSER_CHROMIUM_HOST_RULES= + diff --git a/mcp/browser-mcp/Dockerfile b/mcp/browser-mcp/Dockerfile new file mode 100644 index 000000000..ac30353b9 --- /dev/null +++ b/mcp/browser-mcp/Dockerfile @@ -0,0 +1,27 @@ +FROM python:3.11-slim + +# Install system dependencies for Playwright +RUN apt-get update && apt-get install -y \ + wget \ + gnupg \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Copy requirements and install Python dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Install Playwright browsers +RUN playwright install chromium +RUN playwright install-deps chromium + +# Copy application code +COPY server.py . + +# Expose port +EXPOSE 3002 + +# Run the server +CMD ["python", "server.py"] \ No newline at end of file diff --git a/mcp/browser-mcp/README.md b/mcp/browser-mcp/README.md new file mode 100644 index 000000000..9361bb8e6 --- /dev/null +++ b/mcp/browser-mcp/README.md @@ -0,0 +1,191 @@ +# Browser Agent MCP Server + +A Model Context Protocol (MCP) server that provides browser automation capabilities using Playwright. This server enables AI assistants to interact with web applications through a persistent headless browser instance. + +## Purpose + +This MCP server allows AI assistants to: +- Navigate to web pages and wait for dynamic content to load +- Take screenshots of pages (viewport or full-page) +- Interact with page elements (click, hover, fill forms) +- Extract text and HTML content from pages +- Monitor browser console logs (errors, warnings, info) +- Automate user workflows for testing and validation + +## Available Tools + +### navigate +Navigate to a specific URL with optional timeout for SPA data loading. + +**Parameters:** +- `url` (required): The URL to navigate to +- `timeout` (optional): Additional wait time in milliseconds after network idle (default: 5000) + +### screenshot +Take a screenshot of the current page and return as image. + +**Parameters:** +- `full_page` (optional): Take full page screenshot (default: false for viewport-only) + +### click +Click an element on the page using a CSS selector. + +**Parameters:** +- `selector` (required): CSS selector for the element to click +- `force` (optional): Force click even if element is not visible (default: false) + +### hover +Hover over an element to trigger hover states (like showing hidden buttons). + +**Parameters:** +- `selector` (required): CSS selector for the element to hover over + +### fill +Fill out a form field with a value. + +**Parameters:** +- `selector` (required): CSS selector for the input field +- `value` (required): Value to fill into the field + +### get_content +Get the text content of the current page. + +**Parameters:** None + +### get_console_logs +Get browser console logs (errors, warnings, info). + +**Parameters:** +- `level` (optional): Filter by log level: 'error', 'warning', 'info', 'log', or 'all' (default: 'all') +- `clear` (optional): Clear logs after reading (default: true) + +### get_html +Get the HTML content of a specific element or the entire page. + +**Parameters:** +- `selector` (optional): CSS selector for the element (omit to get full page HTML) + +## Architecture + +- **Framework**: FastAPI for HTTP server +- **Browser**: Playwright with Chromium (headless mode) +- **Protocol**: MCP over HTTP using JSON-RPC 2.0 +- **Internal Port**: 3002 +- **Default External Port**: 3336 (configurable via `BROWSER_MCP_PORT`) + +## Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `HOST` | `0.0.0.0` | Server listen address | +| `PORT` | `3002` | Server listen port (internal) | +| `BROWSER_MCP_PORT` | `3336` | External port mapping (docker-compose) | +| `BROWSER_CHROMIUM_HOST_RULES` | _(empty)_ | Chromium `--host-resolver-rules` flag value | + +### `BROWSER_CHROMIUM_HOST_RULES` + +This variable controls how Chromium resolves hostnames inside the container. It maps directly to Chromium's `--host-resolver-rules` launch flag. + +**When to use it:** +- When the app under test relies on hostnames that resolve differently inside Docker vs. on the host +- Commonly needed when authentication services (Keycloak, etc.) run in Docker + +**Examples:** + +| Scenario | Value | +|----------|-------| +| App runs locally, no Docker networking needed | _(leave empty)_ | +| Keycloak in Docker network, app redirects to `localhost` | `MAP localhost keycloak` | +| Custom auth server in Docker | `MAP auth.myapp.local my-auth-container` | +| Multiple host mappings | `MAP localhost keycloak, MAP api.local api-server` | + +## Configuration + +### Standalone (no Docker network needed) + +For apps running on the host machine (accessible via `host.docker.internal` or `localhost`): + +```yaml +# .env +BROWSER_CHROMIUM_HOST_RULES= +``` + +No `docker-compose.override.yml` needed. + +### With Docker network (e.g. Keycloak) + +When the browser needs to reach services in another Docker Compose network, create a `docker-compose.override.yml`: + +```yaml +# docker-compose.override.yml +services: + browser-mcp: + networks: + - my_project_default + +networks: + my_project_default: + external: true +``` + +And set the host rules in `.env`: +```env +BROWSER_CHROMIUM_HOST_RULES=MAP localhost keycloak +``` + +### MCP Client Configuration + +In your project's `.mcp.json`: + +```json +{ + "mcpServers": { + "browser": { + "type": "http", + "url": "http://localhost:3336/mcp" + } + } +} +``` + +## Docker Commands + +Build and start: +```bash +docker-compose build browser-mcp +docker-compose up -d browser-mcp +``` + +View logs: +```bash +docker-compose logs -f browser-mcp +``` + +Restart: +```bash +docker-compose restart browser-mcp +``` + +## Troubleshooting + +### Timeout Issues +- Default timeout is 30 seconds +- Use `get_html` to inspect actual DOM structure instead of guessing selectors +- For hover-triggered elements, use `hover` first, then `click` +- Use `force: true` on click only when necessary + +### Connection Refused +- Ensure the dev server listens on `0.0.0.0`, not `127.0.0.1` +- If running in Docker without `network_mode: host`, use `host.docker.internal` or the correct container hostname +- Verify the container is running: `docker ps | grep browser-mcp` + +### Authentication Redirects Fail +- If the app redirects to an auth server running in Docker (e.g. Keycloak), set `BROWSER_CHROMIUM_HOST_RULES` to map the hostname +- Ensure the browser-mcp container is on the same Docker network as the auth server (via `docker-compose.override.yml`) + +### Screenshot Token Overflow +- Use viewport-only screenshots (default) instead of full-page + +## Health Check + +GET `/health` - Returns server health status and browser initialization state diff --git a/mcp/browser-mcp/requirements.txt b/mcp/browser-mcp/requirements.txt new file mode 100644 index 000000000..5ea4afbfd --- /dev/null +++ b/mcp/browser-mcp/requirements.txt @@ -0,0 +1,5 @@ +fastapi>=0.104.0 +uvicorn[standard]>=0.24.0 +playwright>=1.40.0 +mcp>=0.9.0 +starlette>=0.27.0 diff --git a/mcp/browser-mcp/server.py b/mcp/browser-mcp/server.py new file mode 100644 index 000000000..dc0822e9d --- /dev/null +++ b/mcp/browser-mcp/server.py @@ -0,0 +1,439 @@ +#!/usr/bin/env python3 +""" +Browser Agent MCP Server +Provides browser automation tools via HTTP using Playwright +""" +import asyncio +import base64 +import logging +from contextlib import asynccontextmanager +from fastapi import FastAPI, Request +from mcp.server import Server +from mcp.types import TextContent, ImageContent, Tool +from playwright.async_api import async_playwright, Browser, BrowserContext, Page +from typing import Optional + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# Global browser state +browser_instance: Optional[Browser] = None +browser_context: Optional[BrowserContext] = None +current_page: Optional[Page] = None +playwright_instance = None +console_logs: list = [] + + +async def initialize_browser(): + """Initialize Playwright browser instance""" + global browser_instance, browser_context, current_page, playwright_instance + + if browser_instance is None: + logger.info("Initializing Playwright browser...") + playwright_instance = await async_playwright().start() + # Resolve localhost to the Docker gateway so the browser can reach + # services exposed on the host (e.g. Keycloak on localhost:8080). + import os + host_rules = os.getenv("CHROMIUM_HOST_RULES", "") + launch_args = [] + if host_rules: + launch_args.append(f"--host-resolver-rules={host_rules}") + browser_instance = await playwright_instance.chromium.launch( + headless=True, + args=launch_args + ) + browser_context = await browser_instance.new_context(ignore_https_errors=True) + current_page = await browser_context.new_page() + # Capture console logs + current_page.on("console", lambda msg: console_logs.append({ + "type": msg.type, + "text": msg.text, + "url": msg.location.get("url", "") if hasattr(msg, "location") and msg.location else "" + })) + current_page.set_default_timeout(30000) + logger.info("Browser initialized successfully") + + +async def cleanup_browser(): + """Cleanup browser resources""" + global browser_instance, browser_context, current_page, playwright_instance + + if current_page: + await current_page.close() + current_page = None + if browser_context: + await browser_context.close() + browser_context = None + if browser_instance: + await browser_instance.close() + browser_instance = None + if playwright_instance: + await playwright_instance.stop() + playwright_instance = None + logger.info("Browser cleaned up") + + +@asynccontextmanager +async def lifespan(app: FastAPI): + """Manage application lifespan""" + await initialize_browser() + yield + await cleanup_browser() + + +# Create FastAPI app +app = FastAPI(lifespan=lifespan) + +# Create MCP server +mcp_server = Server("browser-agent") + + +@mcp_server.list_tools() +async def list_tools() -> list[Tool]: + """List available browser automation tools""" + return [ + Tool( + name="navigate", + description="Navigate to a specific URL", + inputSchema={ + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to navigate to" + }, + "timeout": { + "type": "number", + "description": "Additional wait time in milliseconds after network idle (default: 5000)" + } + }, + "required": ["url"] + } + ), + Tool( + name="screenshot", + description="Take a screenshot of the current page and return as image", + inputSchema={ + "type": "object", + "properties": { + "full_page": { + "type": "boolean", + "description": "Take full page screenshot (default: false, viewport only for smaller size)" + } + }, + "required": [] + } + ), + Tool( + name="click", + description="Click an element on the page using a CSS selector", + inputSchema={ + "type": "object", + "properties": { + "selector": { + "type": "string", + "description": "CSS selector for the element to click" + }, + "force": { + "type": "boolean", + "description": "Force click even if element is not visible (default: false). Use sparingly - prefer hover + click for proper user simulation." + } + }, + "required": ["selector"] + } + ), + Tool( + name="hover", + description="Hover over an element to trigger hover states (like showing hidden buttons)", + inputSchema={ + "type": "object", + "properties": { + "selector": { + "type": "string", + "description": "CSS selector for the element to hover over" + } + }, + "required": ["selector"] + } + ), + Tool( + name="fill", + description="Fill out a form field with a value", + inputSchema={ + "type": "object", + "properties": { + "selector": { + "type": "string", + "description": "CSS selector for the input field" + }, + "value": { + "type": "string", + "description": "Value to fill into the field" + } + }, + "required": ["selector", "value"] + } + ), + Tool( + name="get_content", + description="Get the text content of the current page", + inputSchema={ + "type": "object", + "properties": {}, + "required": [] + } + ), + Tool( + name="get_console_logs", + description="Get browser console logs (errors, warnings, info). Optionally filter by type and clear after reading.", + inputSchema={ + "type": "object", + "properties": { + "level": { + "type": "string", + "description": "Filter by log level: 'error', 'warning', 'info', 'log', or 'all' (default: 'all')" + }, + "clear": { + "type": "boolean", + "description": "Clear logs after reading (default: true)" + } + }, + "required": [] + } + ), + Tool( + name="get_html", + description="Get the HTML content of a specific element or the entire page", + inputSchema={ + "type": "object", + "properties": { + "selector": { + "type": "string", + "description": "CSS selector for the element (optional, omit to get full page HTML)" + } + }, + "required": [] + } + ) + ] + + +@mcp_server.call_tool() +async def call_tool(name: str, arguments: dict) -> list[TextContent]: + """Handle tool calls""" + global current_page + + if current_page is None: + await initialize_browser() + + try: + if name == "navigate": + url = arguments["url"] + timeout = arguments.get("timeout", 5000) # Default 5 seconds additional wait + logger.info(f"Navigating to: {url}") + await current_page.goto(url, wait_until="domcontentloaded") + # Additional wait for SPAs to load data + if timeout > 0: + await current_page.wait_for_timeout(timeout) + return [TextContent( + type="text", + text=f"Successfully navigated to {url}" + )] + + elif name == "screenshot": + full_page = arguments.get("full_page", False) + logger.info(f"Taking screenshot (full_page={full_page})") + screenshot_bytes = await current_page.screenshot(full_page=full_page) + screenshot_base64 = base64.b64encode(screenshot_bytes).decode('utf-8') + return [ImageContent( + type="image", + data=screenshot_base64, + mimeType="image/png" + )] + + elif name == "click": + selector = arguments["selector"] + force = arguments.get("force", False) + logger.info(f"Clicking element: {selector} (force={force})") + await current_page.click(selector, force=force) + return [TextContent( + type="text", + text=f"Successfully clicked element: {selector}" + )] + + elif name == "hover": + selector = arguments["selector"] + logger.info(f"Hovering over element: {selector}") + await current_page.hover(selector) + return [TextContent( + type="text", + text=f"Successfully hovered over element: {selector}" + )] + + elif name == "fill": + selector = arguments["selector"] + value = arguments["value"] + logger.info(f"Filling field {selector} with value") + await current_page.fill(selector, value) + return [TextContent( + type="text", + text=f"Successfully filled field: {selector}" + )] + + elif name == "get_content": + logger.info("Getting page content") + # Get inner text for better readability + text_content = await current_page.evaluate("document.body.innerText") + return [TextContent( + type="text", + text=text_content + )] + + elif name == "get_console_logs": + level = arguments.get("level", "all") + clear = arguments.get("clear", True) + logger.info(f"Getting console logs (level={level}, clear={clear})") + if level == "all": + filtered = console_logs[:] + else: + filtered = [l for l in console_logs if l["type"] == level] + if clear: + console_logs.clear() + if not filtered: + return [TextContent(type="text", text="No console logs captured.")] + lines = [f"[{l['type']}] {l['text']}" for l in filtered] + return [TextContent(type="text", text="\n".join(lines))] + + elif name == "get_html": + selector = arguments.get("selector") + if selector: + logger.info(f"Getting HTML for selector: {selector}") + html_content = await current_page.evaluate( + f"document.querySelector('{selector}')?.outerHTML || 'Element not found'" + ) + else: + logger.info("Getting full page HTML") + html_content = await current_page.content() + return [TextContent( + type="text", + text=html_content + )] + + else: + raise ValueError(f"Unknown tool: {name}") + + except Exception as e: + logger.error(f"Error executing tool {name}: {str(e)}") + return [TextContent( + type="text", + text=f"Error: {str(e)}" + )] + + +@app.post("/mcp") +async def handle_mcp(request: Request): + """Handle MCP protocol messages via HTTP""" + body = await request.json() + logger.info(f"Received MCP request: {body.get('method', 'unknown')}") + + # Create a simple in-memory session for HTTP transport + async def read_message(): + return body + + results = [] + async def write_message(msg): + results.append(msg) + + # Handle the request based on method + method = body.get("method") + + if method == "initialize": + return { + "jsonrpc": "2.0", + "id": body.get("id"), + "result": { + "protocolVersion": "2024-11-05", + "capabilities": { + "tools": {} + }, + "serverInfo": { + "name": "browser-agent", + "version": "1.0.0" + } + } + } + + elif method == "tools/list": + tools = await list_tools() + return { + "jsonrpc": "2.0", + "id": body.get("id"), + "result": { + "tools": [ + { + "name": tool.name, + "description": tool.description, + "inputSchema": tool.inputSchema + } + for tool in tools + ] + } + } + + elif method == "tools/call": + params = body.get("params", {}) + tool_name = params.get("name") + arguments = params.get("arguments", {}) + + result = await call_tool(tool_name, arguments) + + # Serialize content based on type + content_list = [] + for content in result: + if content.type == "image": + content_list.append({ + "type": "image", + "data": content.data, + "mimeType": content.mimeType + }) + else: + content_list.append({ + "type": content.type, + "text": content.text + }) + + return { + "jsonrpc": "2.0", + "id": body.get("id"), + "result": { + "content": content_list + } + } + + else: + return { + "jsonrpc": "2.0", + "id": body.get("id"), + "error": { + "code": -32601, + "message": f"Method not found: {method}" + } + } + + +@app.get("/health") +async def health(): + """Health check endpoint""" + return {"status": "healthy", "browser_initialized": browser_instance is not None} + + +if __name__ == "__main__": + import uvicorn + import os + + host = os.getenv("HOST", "0.0.0.0") + port = int(os.getenv("PORT", "3002")) + + logger.info(f"Starting Browser Agent MCP Server on {host}:{port}") + uvicorn.run(app, host=host, port=port) \ No newline at end of file diff --git a/mcp/chrome-devtools-mcp/README.md b/mcp/chrome-devtools-mcp/README.md new file mode 100644 index 000000000..1653c57ab --- /dev/null +++ b/mcp/chrome-devtools-mcp/README.md @@ -0,0 +1,97 @@ +# Chrome DevTools MCP + +Chrome DevTools MCP provides deep browser automation via the Chrome DevTools Protocol — beyond standard Playwright navigation: performance profiling, network inspection, console access, and device emulation. + +## Type + +**stdio (npx)** — no Docker container needed. Runs on-demand via npx. + +## Installation + +No installation required. The server starts automatically via npx when your AI client connects. + +**Prerequisite**: Chrome or Chromium must be installed on the system and accessible in PATH, or launched separately on a known debugging port. + +## Client Configuration + +### Claude Desktop / Claude Code (`.mcp.json`) + +```json +"chrome-devtools": { + "type": "stdio", + "command": "npx", + "args": ["-y", "chrome-devtools-mcp@latest", "--no-usage-statistics"] +} +``` + +### Opencode (`opencode.json`) + +```json +"chrome-devtools": { + "type": "local", + "command": ["npx", "-y", "chrome-devtools-mcp@latest", "--no-usage-statistics"] +} +``` + +### GitHub Copilot VS Code (`mcp.json`) + +```json +"chrome-devtools": { + "type": "stdio", + "command": "npx", + "args": ["-y", "chrome-devtools-mcp@latest", "--no-usage-statistics"] +} +``` + +## Environment Variables + +No environment variables required. + +## Available Tools + +| Tool | Description | +|------|-------------| +| `navigate` | Navigate to a URL | +| `screenshot` | Take a full-page or viewport screenshot | +| `click` | Click on a page element | +| `hover` | Hover over a page element | +| `fill` | Fill an input field | +| `get_content` | Extract text content from the page | +| `get_html` | Get the full HTML source | +| `get_console_messages` | Retrieve browser console output | +| `get_network_requests` | Inspect network requests and responses | +| `performance_start_trace` | Start a performance trace | +| `performance_stop_trace` | Stop trace and retrieve results | +| `performance_analyze_insight` | AI-powered performance analysis | +| `emulate` | Emulate a device (mobile, tablet, etc.) | +| `evaluate_script` | Execute arbitrary JavaScript in the page | +| `lighthouse_audit` | Run a Lighthouse audit on a URL | +| `take_memory_snapshot` | Capture a heap memory snapshot | + +## Comparison with Browser MCP + +| Feature | Browser MCP (port 3336) | Chrome DevTools MCP | +|---------|------------------------|---------------------| +| Type | HTTP/Docker | stdio/npx | +| Navigation & interaction | Yes | Yes | +| Screenshots | Yes | Yes | +| Console logs | Yes | Yes | +| Network inspection | No | Yes | +| Performance traces | No | Yes | +| Lighthouse audits | No | Yes | +| Memory snapshots | No | Yes | +| Device emulation | No | Yes | +| JavaScript execution | No | Yes | + +Use **Browser MCP** for standard web automation. Use **Chrome DevTools MCP** when you need deep DevTools access (performance, network, memory, emulation). + +## Troubleshooting + +- **Chrome not found**: Install Chrome or Chromium and ensure it is on the system PATH +- **Connection refused**: The server may need to launch Chrome with `--remote-debugging-port=9222` +- **Timeout errors**: Some operations (performance traces, Lighthouse) take longer — these are expected + +## References + +- [GitHub: ChromeDevTools/chrome-devtools-mcp](https://github.com/ChromeDevTools/chrome-devtools-mcp) +- [npm: chrome-devtools-mcp](https://www.npmjs.com/package/chrome-devtools-mcp) \ No newline at end of file diff --git a/mcp/context7-mcp/Dockerfile b/mcp/context7-mcp/Dockerfile new file mode 100644 index 000000000..94127756b --- /dev/null +++ b/mcp/context7-mcp/Dockerfile @@ -0,0 +1,28 @@ +FROM golang:1.26-alpine AS builder + +WORKDIR /build + +# Install git for go get +RUN apk add --no-cache git + +# Clone and build context7-http +RUN git clone https://github.com/lrstanley/context7-http.git . && \ + go build -o context7-http + +FROM alpine:latest + +WORKDIR /app + +# Copy binary from builder +COPY --from=builder /build/context7-http /usr/local/bin/context7-http + +# Expose port +EXPOSE 3002 + +# Set default environment variables +ENV HOST=0.0.0.0 +ENV PORT=3002 + +# Run the context7-http server +# This provides HTTP streamable transport for Context7 +CMD ["context7-http", "--bind-addr", "0.0.0.0:3002"] \ No newline at end of file diff --git a/mcp/docker-compose.yml b/mcp/docker-compose.yml new file mode 100644 index 000000000..567ea1b0b --- /dev/null +++ b/mcp/docker-compose.yml @@ -0,0 +1,49 @@ +services: + context7-mcp: + build: + context: ./context7-mcp + dockerfile: Dockerfile + container_name: context7-mcp + stdin_open: true + environment: + - HOST=0.0.0.0 + - PORT=3002 + - CONTEXT7_API_KEY=${CONTEXT7_API_KEY} + ports: + - "127.0.0.1:3338:3002" + restart: unless-stopped + + browser-mcp: + build: + context: ./browser-mcp + dockerfile: Dockerfile + container_name: browser-mcp + stdin_open: true + environment: + - HOST=0.0.0.0 + - PORT=3002 + # Optional: Chromium host resolver rules for Docker networking + # Example: "MAP localhost keycloak" to route localhost to a Docker service + # Leave empty for local/non-Docker apps or when using network_mode=host + - CHROMIUM_HOST_RULES=${BROWSER_CHROMIUM_HOST_RULES:-} + ports: + - "127.0.0.1:3336:3002" + restart: unless-stopped + shm_size: '2gb' + + # GitHub MCP Server - official GitHub integration + github-mcp: + image: ghcr.io/github/github-mcp-server + container_name: github-mcp + command: ["http", "--port", "8082"] + environment: + # Optional: restrict enabled toolsets (comma-separated) + # Available: repos,issues,pull_requests,actions,code_security,experiments + - GITHUB_TOOLSETS=${GITHUB_TOOLSETS:-repos,issues,pull_requests,actions} + # Optional: GitHub Enterprise Server or ghe.com host (leave empty for github.com) + - GITHUB_HOST=${GITHUB_HOST:-} + # Optional: enable read-only mode + - GITHUB_READ_ONLY=${GITHUB_READ_ONLY:-false} + ports: + - "127.0.0.1:3344:8082" + restart: unless-stopped \ No newline at end of file diff --git a/mcp/github-mcp/README.md b/mcp/github-mcp/README.md new file mode 100644 index 000000000..b0fe4f32d --- /dev/null +++ b/mcp/github-mcp/README.md @@ -0,0 +1,60 @@ +# GitHub MCP Server + +Official GitHub MCP Server from GitHub, providing GitHub API integration via Docker. + +## Purpose + +Enables AI assistants to interact with GitHub repositories, issues, pull requests, and GitHub Actions. + +## Docker Configuration + +**Image**: `ghcr.io/github/github-mcp-server` +**Port**: 3344 (internal: 8082) +**Authentication**: Personal Access Token via `Authorization: Bearer` header + +## Environment Variables + +| Variable | Required | Description | +|----------|----------|-------------| +| `GITHUB_PERSONAL_ACCESS_TOKEN` | Yes | GitHub Personal Access Token (classic or fine-grained) | +| `GITHUB_HOST` | No | Custom GitHub Enterprise host (leave empty for github.com) | +| `GITHUB_TOOLSETS` | No | Comma-separated list of toolsets to enable (default: all). Options: `repos`, `issues`, `pull_requests`, `actions`, `code_security`, `experiments` | +| `GITHUB_READ_ONLY` | No | Set to `true` to disable write operations (default: `false`) | + +## Token Permissions + +The token requires these GitHub permissions: +- `repo` — Full repository access +- `read:org` — Read organization data +- `read:user` — Read user profile +- `workflow` — Manage GitHub Actions (if using `actions` toolset) + +## Available Toolsets + +| Toolset | Tools | +|---------|-------| +| `repos` | Create/list/get/fork/delete repos, manage branches, commits, tags, releases | +| `issues` | Create/list/get/update/close issues, manage labels and assignees | +| `pull_requests` | Create/list/get/merge PRs, manage reviews and comments | +| `actions` | List/get/trigger/cancel workflow runs | +| `code_security` | List code scanning alerts and secret scanning alerts | +| `experiments` | Experimental features | + +## Usage Examples + +``` +# List open issues +List all open issues in repository owner/repo + +# Create a pull request +Create a PR from branch feature/xyz to main with title "Add feature XYZ" + +# Check workflow runs +Show the latest workflow runs for the repository +``` + +## Troubleshooting + +- **401 Unauthorized**: Check that `GITHUB_PERSONAL_ACCESS_TOKEN` is valid and not expired +- **403 Forbidden**: Token lacks required permissions — add `repo` scope +- **Container not starting**: Check that port 3344 is not in use: `docker ps -a` diff --git a/mcp/github-mcp/github-api.http b/mcp/github-mcp/github-api.http new file mode 100644 index 000000000..d2d1fceaa --- /dev/null +++ b/mcp/github-mcp/github-api.http @@ -0,0 +1,4 @@ +GET https://api.github.com/user +Accept: application/vnd.github+json +Authorization: Bearer {{github_token}} +X-GitHub-Api-Version: 2022-11-28 \ No newline at end of file diff --git a/setup-ai-assistant.md b/setup-ai-assistant.md new file mode 100644 index 000000000..ff64bce50 --- /dev/null +++ b/setup-ai-assistant.md @@ -0,0 +1,192 @@ +# Setup Claude Code + +## Installation + +Follow this guide to install claude: + +### Windows + +#### Prerequisites + +- [Git for Windows](https://git-scm.com/downloads/win) (required) + +#### Install + +**PowerShell:** + +```powershell +irm https://claude.ai/install.ps1 | iex +``` + +**Command Prompt:** + +```batch +curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd +``` + +Native installs **update automatically** in the background. + +Add the following path to the PATH variable in your system variables as instructed by the installer: `C:\Users\<username>\.local\bin` + +Verify: + +```powershell +claude --version +``` +### Linux + +```bash +curl -fsSL https://claude.ai/install.sh | bash +``` + +The installer places the `claude` binary in `~/.local/bin` (or similar) and adds it to your PATH automatically. +Native installs **update automatically** in the background. + +Verify the installation: + +```bash +claude --version +``` + +## Settings + +There should be a directory `~/.claude`. If not, create one. +Create/Edit `settings.json` in this directory: +Be sure that `NODE_EXTRA_CA_CERTS` points to the current root ca of XX. + +```json +{ + "env": { + "ANTHROPIC_BASE_URL": "https://litellm.prod.ki-plattform.exxcellent.de/", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "eu/claude-4.6-opus", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "eu/claude-4.6-sonnet", + "ANTHROPIC_DEFAULT_HAIKU_MODEL": "eu/gpt-5.1", + "CLAUDE_CODE_SUBAGENT_MODEL": "eu/claude-4.6-sonnet", + "NODE_EXTRA_CA_CERTS": "C:\\Users\\xx\\certs\\xx_root_2024.crt" + }, + "includeCoAuthoredBy": false, + "permissions": { + "allow": [ + "Bash(git:*)", + "Bash(ls:*)", + "Bash(cat:*)", + "Bash(grep:*)", + "Bash(find:*)", + "WebSearch", + "WebFetch(domain:github.com)", + "WebFetch(domain:docs.anthropic.com)" + ], + "deny": [ + "Bash(rm -rf:*)", + "Bash(sudo:*)", + "Bash(curl * | bash:*)", + "Bash(wget *)", + "Bash(ssh *)", + "Bash(scp *)", + "Bash(sftp *)", + "Read(**/.env)", + "Read(**/secrets/**)", + "Read(**/*.pem)", + "Read(**/*.key)", + "Read(**/.ssh/**)", + "Read(**/id_rsa)" + ], + "disableBypassPermissionsMode": "disable", + "_comment": "The 'disableBypassPermissionsMode' disables the --dangerously-skip-permissions command-line flag. You can customize the allow and deny lists as needed." + }, + "statusLine": { + "type": "command", + "command": "branch=$(git branch --show-current 2>/dev/null || echo 'no-git'); echo \"$(basename $(pwd)) ($branch)\"" + }, + "disableAutoMode": "disable", + "theme": "light", + "_comment": "The 'disableAutoMode' setting prevents the model from using auto mode. Auto mode lets Claude execute without permission prompts.", + "statsigFeatureGates": { + "telemetry_disabled": true + } +} + +``` + +In the project you can find a directory `.claude`. Here, create a `settings.local.json` +of the following form: + +```json +{ + "env": { + "ANTHROPIC_BASE_URL": "https://litellm.prod.ki-plattform.exxcellent.de/", + "ANTHROPIC_AUTH_TOKEN": "...", + "GITHUB_PERSONAL_ACCESS_TOKEN": "..." + }, + "permissions": { + "allow": [] + } +} +``` + +## MCP + +Switch to directory `/mcp` and create a `.env` copying the content of the `.env.example`. +You need to get some API keys. If not described otherwise, these need to be set in the `.env` in the directory `mcp`. + +### Context7 + +Go to https://context7.com/dashboard and create some API key. + +### Github + +Go to https://gitlab-ext.exxcellent.de/-/user_settings/personal_access_tokens?page=1&state=active&sort=expires_asc and +create a new personal access token. You need read & write access. This access token must be setup in +`.claude/settings.local.json`: + +## Token Permissions + +The token requires these GitHub permissions: +- `repo` — Full repository access +- `read:org` — Read organization data +- `read:user` — Read user profile +- `workflow` — Manage GitHub Actions (if using `actions` toolset) + + +``` +"env": { + "ANTHROPIC_BASE_URL": "...", + "ANTHROPIC_AUTH_TOKEN": "...", + "GITLAB_PERSONAL_ACCESS_TOKEN": "..." + } +``` + +### Verify + +On the first startup, you must export properties as env vars: + +Windows: + +```shell +$env:ANTHROPIC_AUTH_TOKEN = "your_token_here" +$env:GITLAB_PERSONAL_ACCESS_TOKEN = "gitlab personal access token" +``` + +Linux: + +```shell +export ANTHROPIC_AUTH_TOKEN=<your-litellm-api-key> +export GITLAB_PERSONAL_ACCESS_TOKEN=<gitlab personal access token> +``` + +The command + +``` +claude mcp list +``` + +lists all (pending) mcp servers. + +Start claude with the command + +``` +claude +``` + + + From 368e1803d578e4da3ad0b01be569812ab5e3b097 Mon Sep 17 00:00:00 2001 From: rschneider <97682836+rainer-exxcellent@users.noreply.github.com> Date: Fri, 26 Jun 2026 08:33:18 +0200 Subject: [PATCH 2/2] (chore): claude setup - add .mcp.json.example with "Bearer {{github token}}}" --- .mcp.json.example | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .mcp.json.example diff --git a/.mcp.json.example b/.mcp.json.example new file mode 100644 index 000000000..78ecf3513 --- /dev/null +++ b/.mcp.json.example @@ -0,0 +1,24 @@ +{ + "mcpServers": { + "context7": { + "type": "http", + "url": "http://localhost:3338/mcp" + }, + "browser-mcp": { + "type": "http", + "url": "http://localhost:3336/mcp" + }, + "github-mcp": { + "type": "http", + "url": "http://localhost:3344/", + "headers": { + "Authorization": "Bearer {{github token}}}" + } + }, + "chrome-devtools": { + "type": "stdio", + "command": "npx", + "args": ["-y", "chrome-devtools-mcp@latest", "--no-usage-statistics"] + } + } +} \ No newline at end of file