mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-01-11 08:28:11 -05:00
Compare commits
2 Commits
binary-npx
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4abf0c39e7 | ||
|
|
dd7b9de623 |
17
DEVELOPER.md
17
DEVELOPER.md
@@ -379,6 +379,23 @@ to approve PRs for main. TeamSync is used to create this team from the MDB
|
||||
Group `toolbox-contributors`. Googlers who are developing for MCP-Toolbox
|
||||
but aren't part of the core team should join this group.
|
||||
|
||||
### Issue/PR Triage and SLO
|
||||
After an issue is created, maintainers will assign the following labels:
|
||||
* `Priority` (defaulted to P0)
|
||||
* `Type` (if applicable)
|
||||
* `Product` (if applicable)
|
||||
|
||||
All incoming issues and PRs will follow the following SLO:
|
||||
| Type | Priority | Objective |
|
||||
|-----------------|----------|------------------------------------------------------------------------|
|
||||
| Feature Request | P0 | Must respond within **5 days** |
|
||||
| Process | P0 | Must respond within **5 days** |
|
||||
| Bugs | P0 | Must respond within **5 days**, and resolve/closure within **14 days** |
|
||||
| Bugs | P1 | Must respond within **7 days**, and resolve/closure within **90 days** |
|
||||
| Bugs | P2 | Must respond within **30 days**
|
||||
|
||||
_Types that are not listed in the table do not adhere to any SLO._
|
||||
|
||||
### Releasing
|
||||
|
||||
Toolbox has two types of releases: versioned and continuous. It uses Google
|
||||
|
||||
@@ -134,6 +134,7 @@ sources:
|
||||
# scopes: # Optional: List of OAuth scopes to request.
|
||||
# - "https://www.googleapis.com/auth/bigquery"
|
||||
# - "https://www.googleapis.com/auth/drive.readonly"
|
||||
# maxQueryResultRows: 50 # Optional: Limits the number of rows returned by queries. Defaults to 50.
|
||||
```
|
||||
|
||||
Initialize a BigQuery source that uses the client's access token:
|
||||
@@ -153,6 +154,7 @@ sources:
|
||||
# scopes: # Optional: List of OAuth scopes to request.
|
||||
# - "https://www.googleapis.com/auth/bigquery"
|
||||
# - "https://www.googleapis.com/auth/drive.readonly"
|
||||
# maxQueryResultRows: 50 # Optional: Limits the number of rows returned by queries. Defaults to 50.
|
||||
```
|
||||
|
||||
## Reference
|
||||
@@ -167,3 +169,4 @@ sources:
|
||||
| useClientOAuth | bool | false | If true, forwards the client's OAuth access token from the "Authorization" header to downstream queries. **Note:** This cannot be used with `writeMode: protected`. |
|
||||
| scopes | []string | false | A list of OAuth 2.0 scopes to use for the credentials. If not provided, default scopes are used. |
|
||||
| impersonateServiceAccount | string | false | Service account email to impersonate when making BigQuery and Dataplex API calls. The authenticated principal must have the `roles/iam.serviceAccountTokenCreator` role on the target service account. [Learn More](https://cloud.google.com/iam/docs/service-account-impersonation) |
|
||||
| maxQueryResultRows | int | false | The maximum number of rows to return from a query. Defaults to 50. |
|
||||
|
||||
@@ -19,6 +19,7 @@ sources:
|
||||
location: ${BIGQUERY_LOCATION:}
|
||||
useClientOAuth: ${BIGQUERY_USE_CLIENT_OAUTH:false}
|
||||
scopes: ${BIGQUERY_SCOPES:}
|
||||
maxQueryResultRows: ${BIGQUERY_MAX_QUERY_RESULT_ROWS:50}
|
||||
|
||||
tools:
|
||||
analyze_contribution:
|
||||
|
||||
@@ -89,6 +89,7 @@ type Config struct {
|
||||
UseClientOAuth bool `yaml:"useClientOAuth"`
|
||||
ImpersonateServiceAccount string `yaml:"impersonateServiceAccount"`
|
||||
Scopes StringOrStringSlice `yaml:"scopes"`
|
||||
MaxQueryResultRows int `yaml:"maxQueryResultRows"`
|
||||
}
|
||||
|
||||
// StringOrStringSlice is a custom type that can unmarshal both a single string
|
||||
@@ -127,6 +128,10 @@ func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.So
|
||||
r.WriteMode = WriteModeAllowed
|
||||
}
|
||||
|
||||
if r.MaxQueryResultRows == 0 {
|
||||
r.MaxQueryResultRows = 50
|
||||
}
|
||||
|
||||
if r.WriteMode == WriteModeProtected && r.UseClientOAuth {
|
||||
// The protected mode only allows write operations to the session's temporary datasets.
|
||||
// when using client OAuth, a new session is created every
|
||||
@@ -150,7 +155,7 @@ func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.So
|
||||
Client: client,
|
||||
RestService: restService,
|
||||
TokenSource: tokenSource,
|
||||
MaxQueryResultRows: 50,
|
||||
MaxQueryResultRows: r.MaxQueryResultRows,
|
||||
ClientCreator: clientCreator,
|
||||
}
|
||||
|
||||
@@ -567,7 +572,7 @@ func (s *Source) RunSQL(ctx context.Context, bqClient *bigqueryapi.Client, state
|
||||
}
|
||||
|
||||
var out []any
|
||||
for {
|
||||
for s.MaxQueryResultRows <= 0 || len(out) < s.MaxQueryResultRows {
|
||||
var val []bigqueryapi.Value
|
||||
err = it.Next(&val)
|
||||
if err == iterator.Done {
|
||||
|
||||
@@ -21,9 +21,12 @@ import (
|
||||
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
"github.com/googleapis/genai-toolbox/internal/server"
|
||||
"github.com/googleapis/genai-toolbox/internal/sources/bigquery"
|
||||
"github.com/googleapis/genai-toolbox/internal/testutils"
|
||||
"github.com/googleapis/genai-toolbox/internal/util"
|
||||
)
|
||||
|
||||
func TestParseFromYamlBigQuery(t *testing.T) {
|
||||
@@ -154,6 +157,26 @@ func TestParseFromYamlBigQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with max query result rows example",
|
||||
in: `
|
||||
sources:
|
||||
my-instance:
|
||||
kind: bigquery
|
||||
project: my-project
|
||||
location: us
|
||||
maxQueryResultRows: 10
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-instance": bigquery.Config{
|
||||
Name: "my-instance",
|
||||
Kind: bigquery.SourceKind,
|
||||
Project: "my-project",
|
||||
Location: "us",
|
||||
MaxQueryResultRows: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
@@ -220,6 +243,59 @@ func TestFailParseFromYaml(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitialize_MaxQueryResultRows(t *testing.T) {
|
||||
ctx, err := testutils.ContextWithNewLogger()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
ctx = util.WithUserAgent(ctx, "test-agent")
|
||||
tracer := noop.NewTracerProvider().Tracer("")
|
||||
|
||||
tcs := []struct {
|
||||
desc string
|
||||
cfg bigquery.Config
|
||||
want int
|
||||
}{
|
||||
{
|
||||
desc: "default value",
|
||||
cfg: bigquery.Config{
|
||||
Name: "test-default",
|
||||
Kind: bigquery.SourceKind,
|
||||
Project: "test-project",
|
||||
UseClientOAuth: true,
|
||||
},
|
||||
want: 50,
|
||||
},
|
||||
{
|
||||
desc: "configured value",
|
||||
cfg: bigquery.Config{
|
||||
Name: "test-configured",
|
||||
Kind: bigquery.SourceKind,
|
||||
Project: "test-project",
|
||||
UseClientOAuth: true,
|
||||
MaxQueryResultRows: 100,
|
||||
},
|
||||
want: 100,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
src, err := tc.cfg.Initialize(ctx, tracer)
|
||||
if err != nil {
|
||||
t.Fatalf("Initialize failed: %v", err)
|
||||
}
|
||||
bqSrc, ok := src.(*bigquery.Source)
|
||||
if !ok {
|
||||
t.Fatalf("Expected *bigquery.Source, got %T", src)
|
||||
}
|
||||
if bqSrc.MaxQueryResultRows != tc.want {
|
||||
t.Errorf("MaxQueryResultRows = %d, want %d", bqSrc.MaxQueryResultRows, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeValue(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
cd server-darwin-arm64
|
||||
npm pack .
|
||||
npm publish --access public
|
||||
|
||||
cd ../server-darwin-x64
|
||||
npm pack .
|
||||
npm publish --access public
|
||||
|
||||
cd ../server-linux-x64
|
||||
npm pack .
|
||||
npm publish --access public
|
||||
|
||||
cd ../server-win32-x64
|
||||
npm pack .
|
||||
npm publish --access public
|
||||
@@ -1,6 +0,0 @@
|
||||
# @toolbox-sdk/server-darwin-arm64
|
||||
|
||||
Platform-specific binary for the `toolbox` package on Darwin arm64.
|
||||
This package is automatically installed by the main `@toolbox-sdk/server` package and is not intended to be installed directly.
|
||||
|
||||
For more information, visit the [toolbox npm package](https://www.npmjs.com/package/@toolbox-sdk/server).
|
||||
19
packages/server-darwin-arm64/package-lock.json
generated
19
packages/server-darwin-arm64/package-lock.json
generated
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "@toolbox-sdk/server-darwin-arm64",
|
||||
"version": "0.25.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@toolbox-sdk/server-darwin-arm64",
|
||||
"version": "0.25.0",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "@toolbox-sdk/server-darwin-arm64",
|
||||
"version": "0.25.0",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Google LLC",
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"main": "bin/toolbox",
|
||||
"repository": "googleapis/genai-toolbox",
|
||||
"scripts": {
|
||||
"prepack": "node scripts/downloadBinary.js darwin arm64"
|
||||
},
|
||||
"files": [
|
||||
"bin/toolbox"
|
||||
]
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// 1. Configuration
|
||||
const PLATFORM_MAP = {
|
||||
'linux': 'linux',
|
||||
'darwin': 'darwin',
|
||||
'win32': 'windows'
|
||||
};
|
||||
|
||||
const ARCH_MAP = {
|
||||
'x64': 'amd64',
|
||||
'arm64': 'arm64'
|
||||
};
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 2) {
|
||||
console.error("Usage: node download-binary.js <platform> <arch>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const [targetPlatform, targetArch] = args;
|
||||
|
||||
// 2. Determine Version
|
||||
const version = fs.readFileSync(path.join(process.cwd(), 'version.txt'), 'utf8').trim();
|
||||
|
||||
// 3. Construct URL
|
||||
const gcsPlatform = PLATFORM_MAP[targetPlatform];
|
||||
const gcsArch = ARCH_MAP[targetArch];
|
||||
|
||||
if (!gcsPlatform || !gcsArch) {
|
||||
console.error(`Unsupported platform/arch: ${targetPlatform}/${targetArch}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const extension = targetPlatform === 'win32' ? '.exe' : '';
|
||||
const binaryName = `toolbox${extension}`;
|
||||
const url = `https://storage.googleapis.com/genai-toolbox/v${version}/${gcsPlatform}/${gcsArch}/${binaryName}`;
|
||||
|
||||
// 4. Prepare Output
|
||||
const binDir = path.join(process.cwd(), 'bin');
|
||||
if (!fs.existsSync(binDir)) {
|
||||
fs.mkdirSync(binDir, { recursive: true });
|
||||
}
|
||||
const destPath = path.join(binDir, binaryName);
|
||||
|
||||
if (fs.existsSync(destPath)) {
|
||||
console.log(`[Skipped] Binary already exists at ${destPath}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`[Prepack] Downloading ${binaryName} for ${targetPlatform}/${targetArch}...`);
|
||||
console.log(`[Source] ${url}`);
|
||||
|
||||
// 5. Download Function
|
||||
const file = fs.createWriteStream(destPath);
|
||||
https.get(url, function(response) {
|
||||
if (response.statusCode !== 200) {
|
||||
console.error(`❌ Failed to download. Status Code: ${response.statusCode}`);
|
||||
fs.unlink(destPath, () => {}); // Delete partial file
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
response.pipe(file);
|
||||
|
||||
file.on('finish', () => {
|
||||
file.close(() => {
|
||||
// 6. Make executable (Unix only)
|
||||
if (targetPlatform !== 'win32') {
|
||||
try {
|
||||
execSync(`chmod +x "${destPath}"`);
|
||||
} catch (err) {
|
||||
console.warn("⚠️ Could not set executable permissions (chmod failed).");
|
||||
}
|
||||
}
|
||||
console.log(`✅ Success! Binary saved to ${destPath}`);
|
||||
});
|
||||
});
|
||||
}).on('error', function(err) {
|
||||
fs.unlink(destPath, () => {});
|
||||
console.error(`❌ Download Error: ${err.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
0.25.0
|
||||
@@ -1,6 +0,0 @@
|
||||
# @toolbox-sdk/server-darwin-x64
|
||||
|
||||
Platform-specific binary for the `toolbox` package on Darwin x64.
|
||||
This package is automatically installed by the main `@toolbox-sdk/server` package and is not intended to be installed directly.
|
||||
|
||||
For more information, visit the [toolbox npm package](https://www.npmjs.com/package/@toolbox-sdk/server).
|
||||
19
packages/server-darwin-x64/package-lock.json
generated
19
packages/server-darwin-x64/package-lock.json
generated
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "@toolbox-sdk/server-darwin-x64",
|
||||
"version": "0.25.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@toolbox-sdk/server-darwin-x64",
|
||||
"version": "0.25.0",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "@toolbox-sdk/server-darwin-x64",
|
||||
"version": "0.25.0",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Google LLC",
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"main": "bin/toolbox",
|
||||
"repository": "googleapis/genai-toolbox",
|
||||
"scripts": {
|
||||
"prepack": "node scripts/downloadBinary.js darwin x64"
|
||||
},
|
||||
"files": [
|
||||
"bin/toolbox"
|
||||
]
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// 1. Configuration
|
||||
const PLATFORM_MAP = {
|
||||
'linux': 'linux',
|
||||
'darwin': 'darwin',
|
||||
'win32': 'windows'
|
||||
};
|
||||
|
||||
const ARCH_MAP = {
|
||||
'x64': 'amd64',
|
||||
'arm64': 'arm64'
|
||||
};
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 2) {
|
||||
console.error("Usage: node download-binary.js <platform> <arch>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const [targetPlatform, targetArch] = args;
|
||||
|
||||
// 2. Determine Version
|
||||
const version = fs.readFileSync(path.join(process.cwd(), 'version.txt'), 'utf8').trim();
|
||||
|
||||
// 3. Construct URL
|
||||
const gcsPlatform = PLATFORM_MAP[targetPlatform];
|
||||
const gcsArch = ARCH_MAP[targetArch];
|
||||
|
||||
if (!gcsPlatform || !gcsArch) {
|
||||
console.error(`Unsupported platform/arch: ${targetPlatform}/${targetArch}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const extension = targetPlatform === 'win32' ? '.exe' : '';
|
||||
const binaryName = `toolbox${extension}`;
|
||||
const url = `https://storage.googleapis.com/genai-toolbox/v${version}/${gcsPlatform}/${gcsArch}/${binaryName}`;
|
||||
|
||||
// 4. Prepare Output
|
||||
const binDir = path.join(process.cwd(), 'bin');
|
||||
if (!fs.existsSync(binDir)) {
|
||||
fs.mkdirSync(binDir, { recursive: true });
|
||||
}
|
||||
const destPath = path.join(binDir, binaryName);
|
||||
|
||||
if (fs.existsSync(destPath)) {
|
||||
console.log(`[Skipped] Binary already exists at ${destPath}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`[Prepack] Downloading ${binaryName} for ${targetPlatform}/${targetArch}...`);
|
||||
console.log(`[Source] ${url}`);
|
||||
|
||||
// 5. Download Function
|
||||
const file = fs.createWriteStream(destPath);
|
||||
https.get(url, function(response) {
|
||||
if (response.statusCode !== 200) {
|
||||
console.error(`❌ Failed to download. Status Code: ${response.statusCode}`);
|
||||
fs.unlink(destPath, () => {}); // Delete partial file
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
response.pipe(file);
|
||||
|
||||
file.on('finish', () => {
|
||||
file.close(() => {
|
||||
// 6. Make executable (Unix only)
|
||||
if (targetPlatform !== 'win32') {
|
||||
try {
|
||||
execSync(`chmod +x "${destPath}"`);
|
||||
} catch (err) {
|
||||
console.warn("⚠️ Could not set executable permissions (chmod failed).");
|
||||
}
|
||||
}
|
||||
console.log(`✅ Success! Binary saved to ${destPath}`);
|
||||
});
|
||||
});
|
||||
}).on('error', function(err) {
|
||||
fs.unlink(destPath, () => {});
|
||||
console.error(`❌ Download Error: ${err.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
0.25.0
|
||||
@@ -1,7 +0,0 @@
|
||||
# @toolbox-sdk/server-linux-x64
|
||||
|
||||
Platform-specific binary for the `toolbox` package on Linux x64.
|
||||
This package is automatically installed by the main `@toolbox-sdk/server` package and is not intended to be installed directly.
|
||||
|
||||
For more information, visit the [toolbox npm package](https://www.npmjs.com/package/@toolbox-sdk/server).
|
||||
|
||||
19
packages/server-linux-x64/package-lock.json
generated
19
packages/server-linux-x64/package-lock.json
generated
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "@toolbox-sdk/server-linux-x64",
|
||||
"version": "0.25.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@toolbox-sdk/server-linux-x64",
|
||||
"version": "0.25.0",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "@toolbox-sdk/server-linux-x64",
|
||||
"version": "0.25.0",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Google LLC",
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"main": "bin/toolbox",
|
||||
"repository": "googleapis/genai-toolbox",
|
||||
"scripts": {
|
||||
"prepack": "node scripts/downloadBinary.js linux x64"
|
||||
},
|
||||
"files": [
|
||||
"bin/toolbox"
|
||||
]
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// 1. Configuration
|
||||
const PLATFORM_MAP = {
|
||||
'linux': 'linux',
|
||||
'darwin': 'darwin',
|
||||
'win32': 'windows'
|
||||
};
|
||||
|
||||
const ARCH_MAP = {
|
||||
'x64': 'amd64',
|
||||
'arm64': 'arm64'
|
||||
};
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 2) {
|
||||
console.error("Usage: node download-binary.js <platform> <arch>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const [targetPlatform, targetArch] = args;
|
||||
|
||||
// 2. Determine Version
|
||||
const version = fs.readFileSync(path.join(process.cwd(), 'version.txt'), 'utf8').trim();
|
||||
|
||||
// 3. Construct URL
|
||||
const gcsPlatform = PLATFORM_MAP[targetPlatform];
|
||||
const gcsArch = ARCH_MAP[targetArch];
|
||||
|
||||
if (!gcsPlatform || !gcsArch) {
|
||||
console.error(`Unsupported platform/arch: ${targetPlatform}/${targetArch}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const extension = targetPlatform === 'win32' ? '.exe' : '';
|
||||
const binaryName = `toolbox${extension}`;
|
||||
const url = `https://storage.googleapis.com/genai-toolbox/v${version}/${gcsPlatform}/${gcsArch}/${binaryName}`;
|
||||
|
||||
// 4. Prepare Output
|
||||
const binDir = path.join(process.cwd(), 'bin');
|
||||
if (!fs.existsSync(binDir)) {
|
||||
fs.mkdirSync(binDir, { recursive: true });
|
||||
}
|
||||
const destPath = path.join(binDir, binaryName);
|
||||
|
||||
if (fs.existsSync(destPath)) {
|
||||
console.log(`[Skipped] Binary already exists at ${destPath}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`[Prepack] Downloading ${binaryName} for ${targetPlatform}/${targetArch}...`);
|
||||
console.log(`[Source] ${url}`);
|
||||
|
||||
// 5. Download Function
|
||||
const file = fs.createWriteStream(destPath);
|
||||
https.get(url, function(response) {
|
||||
if (response.statusCode !== 200) {
|
||||
console.error(`❌ Failed to download. Status Code: ${response.statusCode}`);
|
||||
fs.unlink(destPath, () => {}); // Delete partial file
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
response.pipe(file);
|
||||
|
||||
file.on('finish', () => {
|
||||
file.close(() => {
|
||||
// 6. Make executable (Unix only)
|
||||
if (targetPlatform !== 'win32') {
|
||||
try {
|
||||
execSync(`chmod +x "${destPath}"`);
|
||||
} catch (err) {
|
||||
console.warn("⚠️ Could not set executable permissions (chmod failed).");
|
||||
}
|
||||
}
|
||||
console.log(`✅ Success! Binary saved to ${destPath}`);
|
||||
});
|
||||
});
|
||||
}).on('error', function(err) {
|
||||
fs.unlink(destPath, () => {});
|
||||
console.error(`❌ Download Error: ${err.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
0.25.0
|
||||
@@ -1,7 +0,0 @@
|
||||
# @toolbox-sdk/server-win32-x64
|
||||
|
||||
Platform-specific binary for the `toolbox` package on Windows x64.
|
||||
This package is automatically installed by the main `@toolbox-sdk/server` package and is not intended to be installed directly.
|
||||
|
||||
For more information, visit the [toolbox npm package](https://www.npmjs.com/package/@toolbox-sdk/server).
|
||||
|
||||
19
packages/server-win32-x64/package-lock.json
generated
19
packages/server-win32-x64/package-lock.json
generated
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "@toolbox-sdk/server-win32-x64",
|
||||
"version": "0.25.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@toolbox-sdk/server-win32-x64",
|
||||
"version": "0.25.0",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "@toolbox-sdk/server-win32-x64",
|
||||
"version": "0.25.0",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Google LLC",
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"main": "bin/toolbox.exe",
|
||||
"repository": "googleapis/genai-toolbox",
|
||||
"scripts": {
|
||||
"prepack": "node scripts/downloadBinary.js win32 x64"
|
||||
},
|
||||
"files": [
|
||||
"bin/toolbox.exe"
|
||||
]
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// 1. Configuration
|
||||
const PLATFORM_MAP = {
|
||||
'linux': 'linux',
|
||||
'darwin': 'darwin',
|
||||
'win32': 'windows'
|
||||
};
|
||||
|
||||
const ARCH_MAP = {
|
||||
'x64': 'amd64',
|
||||
'arm64': 'arm64'
|
||||
};
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 2) {
|
||||
console.error("Usage: node download-binary.js <platform> <arch>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const [targetPlatform, targetArch] = args;
|
||||
|
||||
// 2. Determine Version
|
||||
const version = fs.readFileSync(path.join(process.cwd(), 'version.txt'), 'utf8').trim();
|
||||
|
||||
// 3. Construct URL
|
||||
const gcsPlatform = PLATFORM_MAP[targetPlatform];
|
||||
const gcsArch = ARCH_MAP[targetArch];
|
||||
|
||||
if (!gcsPlatform || !gcsArch) {
|
||||
console.error(`Unsupported platform/arch: ${targetPlatform}/${targetArch}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const extension = targetPlatform === 'win32' ? '.exe' : '';
|
||||
const binaryName = `toolbox${extension}`;
|
||||
const url = `https://storage.googleapis.com/genai-toolbox/v${version}/${gcsPlatform}/${gcsArch}/${binaryName}`;
|
||||
|
||||
// 4. Prepare Output
|
||||
const binDir = path.join(process.cwd(), 'bin');
|
||||
if (!fs.existsSync(binDir)) {
|
||||
fs.mkdirSync(binDir, { recursive: true });
|
||||
}
|
||||
const destPath = path.join(binDir, binaryName);
|
||||
|
||||
if (fs.existsSync(destPath)) {
|
||||
console.log(`[Skipped] Binary already exists at ${destPath}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`[Prepack] Downloading ${binaryName} for ${targetPlatform}/${targetArch}...`);
|
||||
console.log(`[Source] ${url}`);
|
||||
|
||||
// 5. Download Function
|
||||
const file = fs.createWriteStream(destPath);
|
||||
https.get(url, function(response) {
|
||||
if (response.statusCode !== 200) {
|
||||
console.error(`❌ Failed to download. Status Code: ${response.statusCode}`);
|
||||
fs.unlink(destPath, () => {}); // Delete partial file
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
response.pipe(file);
|
||||
|
||||
file.on('finish', () => {
|
||||
file.close(() => {
|
||||
// 6. Make executable (Unix only)
|
||||
if (targetPlatform !== 'win32') {
|
||||
try {
|
||||
execSync(`chmod +x "${destPath}"`);
|
||||
} catch (err) {
|
||||
console.warn("⚠️ Could not set executable permissions (chmod failed).");
|
||||
}
|
||||
}
|
||||
console.log(`✅ Success! Binary saved to ${destPath}`);
|
||||
});
|
||||
});
|
||||
}).on('error', function(err) {
|
||||
fs.unlink(destPath, () => {});
|
||||
console.error(`❌ Download Error: ${err.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
0.25.0
|
||||
@@ -1,69 +0,0 @@
|
||||
# Toolbox
|
||||
|
||||
A CLI tool for running a toolbox server.
|
||||
|
||||
## Installation
|
||||
|
||||
You can install the toolbox globally:
|
||||
|
||||
```bash
|
||||
npm install -g @toolbox-sdk/server
|
||||
```
|
||||
|
||||
Or run it directly using npx:
|
||||
|
||||
```bash
|
||||
npx @toolbox-sdk/server
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The toolbox requires a `tools.yaml` file in the current working directory to define sources, tools, and prompts.
|
||||
|
||||
### Example `tools.yaml`
|
||||
|
||||
```yaml
|
||||
sources:
|
||||
my-pg-source:
|
||||
kind: postgres
|
||||
host: 127.0.0.1
|
||||
port: 5432
|
||||
database: toolbox_db
|
||||
user: postgres
|
||||
password: password
|
||||
tools:
|
||||
search-hotels-by-name:
|
||||
kind: postgres-sql
|
||||
source: my-pg-source
|
||||
description: Search for hotels based on name.
|
||||
parameters:
|
||||
- name: name
|
||||
type: string
|
||||
description: The name of the hotel.
|
||||
statement: SELECT * FROM hotels WHERE name ILIKE '%' || $1 || '%';
|
||||
prompts:
|
||||
code-review:
|
||||
description: "Asks the LLM to analyze code quality and suggest improvements."
|
||||
messages:
|
||||
- role: "user"
|
||||
content: "Please review the following code for quality, correctness, and potential improvements: \n\n{{.code}}"
|
||||
arguments:
|
||||
- name: "code"
|
||||
description: "The code to review"
|
||||
required: true
|
||||
```
|
||||
|
||||
To learn more on how to configure your toolbox, visit the [official docsite](https://googleapis.github.io/genai-toolbox/getting-started/configure/).
|
||||
|
||||
## Platform Support
|
||||
|
||||
The toolbox automatically handles platform-specific binaries. Supported platforms include:
|
||||
- macOS (arm64, x64)
|
||||
- Linux (x64)
|
||||
- Windows (x64)
|
||||
|
||||
## Resources
|
||||
|
||||
For more information, visit the
|
||||
- [MCP Toolbox repository](https://github.com/googleapis/genai-toolbox)
|
||||
- [Official Documentation](https://googleapis.github.io/genai-toolbox/getting-started/introduction/)
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
const { spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
|
||||
const PLATFORMS = {
|
||||
'darwin-arm64': '@toolbox-sdk/server-darwin-arm64',
|
||||
'darwin-x64': '@toolbox-sdk/server-darwin-x64',
|
||||
'linux-x64': '@toolbox-sdk/server-linux-x64',
|
||||
'win32-x64': '@toolbox-sdk/server-win32-x64'
|
||||
};
|
||||
|
||||
const currentKey = `${os.platform()}-${os.arch()}`;
|
||||
const pkgName = PLATFORMS[currentKey];
|
||||
|
||||
if (!pkgName) {
|
||||
console.error(`Unsupported platform: ${currentKey}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let binPath;
|
||||
try {
|
||||
const pkgJsonPath = require.resolve(`${pkgName}/package.json`);
|
||||
const pkgDir = path.dirname(pkgJsonPath);
|
||||
const binName = os.platform() === 'win32' ? 'toolbox.exe' : 'toolbox';
|
||||
binPath = path.join(pkgDir, 'bin', binName);
|
||||
} catch (e) {
|
||||
console.error(`Binary for ${currentKey} not found. Installation failed?`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (os.platform() !== 'win32') {
|
||||
try {
|
||||
fs.chmodSync(binPath, 0o755);
|
||||
if (os.platform() === 'darwin') {
|
||||
const { execSync } = require('child_process');
|
||||
try {
|
||||
execSync(`xattr -d com.apple.quarantine "${binPath}"`, { stdio: 'ignore' });
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`Could not set execute permissions on ${binPath}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
spawn(binPath, process.argv.slice(2), { stdio: 'inherit' })
|
||||
.on('exit', process.exit);
|
||||
74
packages/server/package-lock.json
generated
74
packages/server/package-lock.json
generated
@@ -1,74 +0,0 @@
|
||||
{
|
||||
"name": "@toolbox-sdk/server",
|
||||
"version": "0.25.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@toolbox-sdk/server",
|
||||
"version": "0.25.0",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"toolbox": "bin/run.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@toolbox-sdk/server-darwin-arm64": "0.25.0",
|
||||
"@toolbox-sdk/server-darwin-x64": "0.25.0",
|
||||
"@toolbox-sdk/server-linux-x64": "0.25.0",
|
||||
"@toolbox-sdk/server-win32-x64": "0.25.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@toolbox-sdk/server-darwin-arm64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@toolbox-sdk/server-darwin-arm64/-/server-darwin-arm64-0.25.0.tgz",
|
||||
"integrity": "sha512-HNAfaDOg2ggnZObJkh5JxoUtfL0WiA2nvDB7qNMdP3130Jgrc9y6rjpGVgN0ntk9zPhioussfbDDgc36jUTq+g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@toolbox-sdk/server-darwin-x64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@toolbox-sdk/server-darwin-x64/-/server-darwin-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-RG7rcfuA8jFylbdT0geXWGKtLBfsqXtwDR2kgJtr5hjdFqjhOwuN6jK0JNnjyHZoBx6xN6cG4nHs25uEWjrD9g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@toolbox-sdk/server-linux-x64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@toolbox-sdk/server-linux-x64/-/server-linux-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-V/yjAcEJD4vxLyHXgkdUM6Wc8agma451O6Ms9loEfcIwwIWnb103UOOiZRd+pLdDViuuSzrhXNEH62vjhIoQUw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@toolbox-sdk/server-win32-x64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@toolbox-sdk/server-win32-x64/-/server-win32-x64-0.25.0.tgz",
|
||||
"integrity": "sha512-QUgPeWvHhD2lLqp4Tk1bDuEOu8l6y5ApsnTScEMIa2GO7G/jRJzkCT1TNZJIUxQMZEEXy38bn9x/fMMtNN9flQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "@toolbox-sdk/server",
|
||||
"version": "0.25.0",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Google LLC",
|
||||
"bin": {
|
||||
"toolbox": "./bin/run.js"
|
||||
},
|
||||
"files": [
|
||||
"bin/run.js"
|
||||
],
|
||||
"repository": "googleapis/genai-toolbox",
|
||||
"optionalDependencies": {
|
||||
"@toolbox-sdk/server-darwin-arm64": "0.25.0",
|
||||
"@toolbox-sdk/server-darwin-x64": "0.25.0",
|
||||
"@toolbox-sdk/server-linux-x64": "0.25.0",
|
||||
"@toolbox-sdk/server-win32-x64": "0.25.0"
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
# How to test on different platforms
|
||||
|
||||
1. Create a local test registry. We are using Verdaccio. During the actual process, we publish to npm instead.
|
||||
```sh
|
||||
# install
|
||||
npm install -g verdaccio
|
||||
|
||||
# start server
|
||||
verdaccio
|
||||
```
|
||||
|
||||
The output will show the config file location and the URL. Open your browser to:
|
||||
http://localhost:4873
|
||||
|
||||
> Note: You can use `npm unpublish @toolbox-sdk/server --force --registry http://localhost:4873` to unpublish the package.
|
||||
|
||||
2. Pack all 4 packages and publish them to the local registry. Go in each package (eg. server-darwin-arm64) and run
|
||||
```sh
|
||||
npm ci --force
|
||||
npm pack .
|
||||
```
|
||||
|
||||
Then publish to the local registry
|
||||
```sh
|
||||
npm publish --registry http://localhost:4873
|
||||
```
|
||||
|
||||
3. Go to the server package and run
|
||||
```sh
|
||||
npm ci --force
|
||||
npm pack .
|
||||
npm publish --registry http://localhost:4873
|
||||
```
|
||||
|
||||
Now, you have published your package.
|
||||
|
||||
4. Now create a new folder (let's call it testing.) Add a tools.yaml file to the folder. It should look like this:
|
||||
```yaml
|
||||
sources:
|
||||
my-pg-source:
|
||||
kind: postgres
|
||||
host: 127.0.0.1
|
||||
port: 5432
|
||||
database: toolbox_db
|
||||
user: postgres
|
||||
password: password
|
||||
tools:
|
||||
search-hotels-by-name:
|
||||
kind: postgres-sql
|
||||
source: my-pg-source
|
||||
description: Search for hotels based on name.
|
||||
parameters:
|
||||
- name: name
|
||||
type: string
|
||||
description: The name of the hotel.
|
||||
statement: SELECT * FROM hotels WHERE name ILIKE '%' || $1 || '%';
|
||||
prompts:
|
||||
code-review:
|
||||
description: "Asks the LLM to analyze code quality and suggest improvements."
|
||||
messages:
|
||||
- role: "user"
|
||||
content: "Please review the following code for quality, correctness, and potential improvements: \n\n{{.code}}"
|
||||
arguments:
|
||||
- name: "code"
|
||||
description: "The code to review"
|
||||
required: true
|
||||
```
|
||||
|
||||
Note: Do not test this directly to the server folder. The registry might pick up local packages instead of global installation.
|
||||
Use a separate testing folder outside the server folder for this testing.
|
||||
|
||||
6. From the testing folder, run
|
||||
```sh
|
||||
npx --registry=http://localhost:4873/ -y @toolbox-sdk/server
|
||||
```
|
||||
|
||||
This should start up the toolbox server with the tools.yaml file.
|
||||
|
||||
7. Run the command to verify that the tools are available:
|
||||
|
||||
```sh
|
||||
curl --location 'http://127.0.0.1:5000/mcp' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "tools/list",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}'
|
||||
|
||||
```
|
||||
@@ -1,21 +0,0 @@
|
||||
cd server-darwin-arm64
|
||||
npm install --force
|
||||
rm -rf bin/
|
||||
git add version.txt package.json package-lock.json
|
||||
|
||||
cd ../server-darwin-x64
|
||||
npm install --force
|
||||
rm -rf bin/
|
||||
git add version.txt package.json package-lock.json
|
||||
|
||||
|
||||
cd ../server-linux-x64
|
||||
npm install --force
|
||||
rm -rf bin/
|
||||
git add version.txt package.json package-lock.json
|
||||
|
||||
|
||||
cd ../server-win32-x64
|
||||
npm install --force
|
||||
rm -rf bin/
|
||||
git add version.txt package.json package-lock.json
|
||||
@@ -1,7 +0,0 @@
|
||||
cd server
|
||||
npm install --package-lock-only
|
||||
git add package.json package-lock.json
|
||||
git commit -m "update dep versions"
|
||||
|
||||
npm pack .
|
||||
npm publish --access public
|
||||
@@ -1,21 +0,0 @@
|
||||
cd server-darwin-arm64
|
||||
npm install --force
|
||||
rm -rf bin/
|
||||
git add version.txt package.json package-lock.json
|
||||
|
||||
cd ../server-darwin-x64
|
||||
npm install --force
|
||||
rm -rf bin/
|
||||
git add version.txt package.json package-lock.json
|
||||
|
||||
|
||||
cd ../server-linux-x64
|
||||
npm install --force
|
||||
rm -rf bin/
|
||||
git add version.txt package.json package-lock.json
|
||||
|
||||
|
||||
cd ../server-win32-x64
|
||||
npm install --force
|
||||
rm -rf bin/
|
||||
git add version.txt package.json package-lock.json
|
||||
Reference in New Issue
Block a user