mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-02-14 09:05:04 -05:00
Compare commits
2 Commits
quickstart
...
feat/add-g
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a2092ffd6 | ||
|
|
5fcf3df882 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -18,6 +18,9 @@ node_modules
|
||||
# coverage
|
||||
.coverage
|
||||
|
||||
# python
|
||||
__pycache__/
|
||||
|
||||
# executable
|
||||
genai-toolbox
|
||||
toolbox
|
||||
108
GEMINI.md
Normal file
108
GEMINI.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# MCP Toolbox Context
|
||||
|
||||
This file (symlinked as `CLAUDE.md` and `AGENTS.md`) provides context and guidelines for AI agents working on the MCP Toolbox for Databases project. It summarizes key information from `CONTRIBUTING.md` and `DEVELOPER.md`.
|
||||
|
||||
## Project Overview
|
||||
|
||||
**MCP Toolbox for Databases** is a Go-based project designed to provide Model Context Protocol (MCP) tools for various data sources and services. It allows Large Language Models (LLMs) to interact with databases and other tools safely and efficiently.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Language:** Go (1.23+)
|
||||
- **Documentation:** Hugo (Extended Edition v0.146.0+)
|
||||
- **Containerization:** Docker
|
||||
- **CI/CD:** GitHub Actions, Google Cloud Build
|
||||
- **Linting:** `golangci-lint`
|
||||
|
||||
## Key Directories
|
||||
|
||||
- `cmd/`: Application entry points.
|
||||
- `internal/sources/`: Implementations of database sources (e.g., Postgres, BigQuery).
|
||||
- `internal/tools/`: Implementations of specific tools for each source.
|
||||
- `tests/`: Integration tests.
|
||||
- `docs/`: Project documentation (Hugo site).
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Go 1.23 or later.
|
||||
- Docker (for building container images and running some tests).
|
||||
- Access to necessary Google Cloud resources for integration testing (if applicable).
|
||||
|
||||
### Building and Running
|
||||
|
||||
1. **Build Binary:** `go build -o toolbox`
|
||||
2. **Run Server:** `go run .` (Listens on port 5000 by default)
|
||||
3. **Run with Help:** `go run . --help`
|
||||
4. **Test Endpoint:** `curl http://127.0.0.1:5000`
|
||||
|
||||
### Testing
|
||||
|
||||
- **Unit Tests:** `go test -race -v ./cmd/... ./internal/...`
|
||||
- **Integration Tests:**
|
||||
- Run specific source tests: `go test -race -v ./tests/<source_dir>`
|
||||
- Example: `go test -race -v ./tests/alloydbpg`
|
||||
- Add new sources to `.ci/integration.cloudbuild.yaml`
|
||||
- **Linting:** `golangci-lint run --fix`
|
||||
|
||||
## Developing Documentation
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Hugo (Extended Edition v0.146.0+)
|
||||
- Node.js (for `npm ci`)
|
||||
|
||||
### Running Local Server
|
||||
|
||||
1. Navigate to `.hugo` directory: `cd .hugo`
|
||||
2. Install dependencies: `npm ci`
|
||||
3. Start server: `hugo server`
|
||||
|
||||
### Versioning Workflows
|
||||
|
||||
1. **Deploy In-development docs**: Merges to main -> `/dev/`.
|
||||
2. **Deploy Versioned Docs**: New Release -> `/<version>/` and root.
|
||||
3. **Deploy Previous Version Docs**: Manual workflow for older versions.
|
||||
|
||||
## Coding Conventions
|
||||
|
||||
### Tool Naming
|
||||
|
||||
- **Tool Name:** `snake_case` (e.g., `list_collections`, `run_query`).
|
||||
- Do *not* include the product name (e.g., avoid `firestore_list_collections`).
|
||||
- **Tool Type:** `kebab-case` (e.g., `firestore-list-collections`).
|
||||
- *Must* include the product name.
|
||||
|
||||
### Branching and Commits
|
||||
|
||||
- **Branch Naming:** `feat/`, `fix/`, `docs/`, `chore/` (e.g., `feat/add-gemini-md`).
|
||||
- **Commit Messages:** [Conventional Commits](https://www.conventionalcommits.org/) format.
|
||||
- Format: `<type>(<scope>): <description>`
|
||||
- Example: `feat(source/postgres): add new connection option`
|
||||
- Types: `feat`, `fix`, `docs`, `chore`, `test`, `ci`, `refactor`, `revert`, `style`.
|
||||
|
||||
## Adding New Features
|
||||
|
||||
### Adding a New Data Source
|
||||
|
||||
1. Create a new directory: `internal/sources/<newdb>`.
|
||||
2. Define `Config` and `Source` structs in `internal/sources/<newdb>/<newdb>.go`.
|
||||
3. Implement `SourceConfig` interface (`SourceConfigType`, `Initialize`).
|
||||
4. Implement `Source` interface (`SourceType`).
|
||||
5. Implement `init()` to register the source.
|
||||
6. Add unit tests in `internal/sources/<newdb>/<newdb>_test.go`.
|
||||
|
||||
### Adding a New Tool
|
||||
|
||||
1. Create a new directory: `internal/tools/<newdb>/<toolname>`.
|
||||
2. Define `Config` and `Tool` structs.
|
||||
3. Implement `ToolConfig` interface (`ToolConfigType`, `Initialize`).
|
||||
4. Implement `Tool` interface (`Invoke`, `ParseParams`, `Manifest`, `McpManifest`, `Authorized`).
|
||||
5. Implement `init()` to register the tool.
|
||||
6. Add unit tests.
|
||||
|
||||
### Adding Documentation
|
||||
|
||||
- Add source documentation to `docs/en/resources/sources/`.
|
||||
- Add tool documentation to `docs/en/resources/tools/`.
|
||||
@@ -20,8 +20,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var goldenKeywords = []string{"Hilton Basel", "Hyatt Regency", "book"}
|
||||
|
||||
func TestQuickstartSample(t *testing.T) {
|
||||
framework := os.Getenv("ORCH_NAME")
|
||||
if framework == "" {
|
||||
@@ -61,10 +59,16 @@ func TestQuickstartSample(t *testing.T) {
|
||||
t.Fatal("Script ran successfully but produced no output.")
|
||||
}
|
||||
|
||||
goldenFile, err := os.ReadFile("../golden.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Could not read golden.txt to check for keywords: %v", err)
|
||||
}
|
||||
|
||||
keywords := strings.Split(string(goldenFile), "\n")
|
||||
var missingKeywords []string
|
||||
outputLower := strings.ToLower(actualOutput)
|
||||
|
||||
for _, keyword := range goldenKeywords {
|
||||
for _, keyword := range keywords {
|
||||
kw := strings.TrimSpace(keyword)
|
||||
if kw != "" && !strings.Contains(outputLower, strings.ToLower(kw)) {
|
||||
missingKeywords = append(missingKeywords, kw)
|
||||
|
||||
3
docs/en/getting-started/quickstart/golden.txt
Normal file
3
docs/en/getting-started/quickstart/golden.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Hilton Basel
|
||||
Hyatt Regency
|
||||
book
|
||||
@@ -25,7 +25,7 @@ const quickstartPath = path.join(orchDir, "quickstart.js");
|
||||
|
||||
const { main: runAgent } = await import(quickstartPath);
|
||||
|
||||
const GOLDEN_KEYWORDS = ["Hilton Basel", "Hyatt Regency", "book"];
|
||||
const GOLDEN_FILE_PATH = path.resolve(__dirname, "../golden.txt");
|
||||
|
||||
describe(`${ORCH_NAME} Quickstart Agent`, () => {
|
||||
let capturedOutput = [];
|
||||
@@ -52,8 +52,11 @@ describe(`${ORCH_NAME} Quickstart Agent`, () => {
|
||||
"Assertion Failed: Script ran successfully but produced no output."
|
||||
);
|
||||
|
||||
const goldenFile = fs.readFileSync(GOLDEN_FILE_PATH, "utf8");
|
||||
const keywords = goldenFile.split("\n").filter((kw) => kw.trim() !== "");
|
||||
const missingKeywords = [];
|
||||
for (const keyword of GOLDEN_KEYWORDS) {
|
||||
|
||||
for (const keyword of keywords) {
|
||||
if (!actualOutput.toLowerCase().includes(keyword.toLowerCase())) {
|
||||
missingKeywords.push(keyword);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,18 @@ module_path = f"python.{ORCH_NAME}.quickstart"
|
||||
quickstart = importlib.import_module(module_path)
|
||||
|
||||
|
||||
GOLDEN_KEYWORDS = ["Hilton Basel", "Hyatt Regency", "book"]
|
||||
@pytest.fixture(scope="module")
|
||||
def golden_keywords():
|
||||
"""Loads expected keywords from the golden.txt file."""
|
||||
golden_file_path = Path("../golden.txt")
|
||||
if not golden_file_path.exists():
|
||||
pytest.fail(f"Golden file not found: {golden_file_path}")
|
||||
try:
|
||||
with open(golden_file_path, 'r') as f:
|
||||
return [line.strip() for line in f.readlines() if line.strip()]
|
||||
except Exception as e:
|
||||
pytest.fail(f"Could not read golden.txt: {e}")
|
||||
|
||||
|
||||
# --- Execution Tests ---
|
||||
class TestExecution:
|
||||
@@ -51,8 +62,8 @@ class TestExecution:
|
||||
"""Test that the script runs and produces no stderr."""
|
||||
assert script_output.err == "", f"Script produced stderr: {script_output.err}"
|
||||
|
||||
def test_keywords_in_output(self, script_output):
|
||||
def test_keywords_in_output(self, script_output, golden_keywords):
|
||||
"""Test that expected keywords are present in the script's output."""
|
||||
output = script_output.out
|
||||
missing_keywords = [kw for kw in GOLDEN_KEYWORDS if kw.lower() not in output.lower()]
|
||||
missing_keywords = [kw for kw in golden_keywords if kw not in output]
|
||||
assert not missing_keywords, f"Missing keywords in output: {missing_keywords}"
|
||||
|
||||
Reference in New Issue
Block a user