@agents-at-scale/ark 0.1.38 → 0.1.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/arkServices.js +9 -0
- package/dist/commands/query/index.js +4 -4
- package/dist/commands/query/index.spec.js +4 -1
- package/dist/lib/arkApiClient.js +1 -0
- package/dist/lib/duration.d.ts +1 -0
- package/dist/lib/duration.js +20 -0
- package/dist/lib/duration.spec.d.ts +1 -0
- package/dist/lib/duration.spec.js +13 -0
- package/dist/lib/errors.d.ts +6 -0
- package/dist/lib/errors.js +6 -0
- package/dist/lib/executeQuery.d.ts +2 -0
- package/dist/lib/executeQuery.js +60 -73
- package/dist/lib/executeQuery.spec.js +66 -33
- package/package.json +1 -1
- package/templates/mcp-server/README.md +1 -0
- package/templates/mcp-server/chart/templates/mcpserver.yaml +3 -0
- package/templates/mcp-server/chart/values.yaml +2 -1
package/dist/arkServices.js
CHANGED
|
@@ -69,6 +69,15 @@ const defaultArkServices = {
|
|
|
69
69
|
k8sDeploymentName: 'ark-controller',
|
|
70
70
|
k8sDevDeploymentName: 'ark-controller-devspace',
|
|
71
71
|
},
|
|
72
|
+
'ark-tenant': {
|
|
73
|
+
name: 'ark-tenant',
|
|
74
|
+
helmReleaseName: 'ark-tenant',
|
|
75
|
+
description: 'Tenant provisioning with RBAC and resource quotas',
|
|
76
|
+
enabled: true,
|
|
77
|
+
category: 'core',
|
|
78
|
+
chartPath: `${REGISTRY_BASE}/ark-tenant`,
|
|
79
|
+
installArgs: [],
|
|
80
|
+
},
|
|
72
81
|
'ark-api': {
|
|
73
82
|
name: 'ark-api',
|
|
74
83
|
helmReleaseName: 'ark-api',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import
|
|
2
|
+
import chalk from 'chalk';
|
|
3
3
|
import { executeQuery, parseTarget } from '../../lib/executeQuery.js';
|
|
4
|
+
import { ExitCodes } from '../../lib/errors.js';
|
|
4
5
|
export function createQueryCommand(_) {
|
|
5
6
|
const queryCommand = new Command('query');
|
|
6
7
|
queryCommand
|
|
@@ -8,11 +9,10 @@ export function createQueryCommand(_) {
|
|
|
8
9
|
.argument('<target>', 'Query target (e.g., model/default, agent/my-agent)')
|
|
9
10
|
.argument('<message>', 'Message to send')
|
|
10
11
|
.action(async (target, message) => {
|
|
11
|
-
// Parse and validate target format
|
|
12
12
|
const parsed = parseTarget(target);
|
|
13
13
|
if (!parsed) {
|
|
14
|
-
|
|
15
|
-
process.exit(
|
|
14
|
+
console.error(chalk.red('Invalid target format. Use: model/name or agent/name etc'));
|
|
15
|
+
process.exit(ExitCodes.CliError);
|
|
16
16
|
}
|
|
17
17
|
await executeQuery({
|
|
18
18
|
targetType: parsed.type,
|
|
@@ -15,6 +15,9 @@ jest.unstable_mockModule('../../lib/output.js', () => ({
|
|
|
15
15
|
const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
|
|
16
16
|
throw new Error('process.exit called');
|
|
17
17
|
}));
|
|
18
|
+
const mockConsoleError = jest
|
|
19
|
+
.spyOn(console, 'error')
|
|
20
|
+
.mockImplementation(() => { });
|
|
18
21
|
const { createQueryCommand } = await import('./index.js');
|
|
19
22
|
describe('createQueryCommand', () => {
|
|
20
23
|
beforeEach(() => {
|
|
@@ -47,7 +50,7 @@ describe('createQueryCommand', () => {
|
|
|
47
50
|
await expect(command.parseAsync(['node', 'test', 'invalid-target', 'Hello'])).rejects.toThrow('process.exit called');
|
|
48
51
|
expect(mockParseTarget).toHaveBeenCalledWith('invalid-target');
|
|
49
52
|
expect(mockExecuteQuery).not.toHaveBeenCalled();
|
|
50
|
-
expect(
|
|
53
|
+
expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Invalid target format'));
|
|
51
54
|
expect(mockExit).toHaveBeenCalledWith(1);
|
|
52
55
|
});
|
|
53
56
|
});
|
package/dist/lib/arkApiClient.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseDuration(duration: string): number;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function parseDuration(duration) {
|
|
2
|
+
const match = duration.match(/^(\d+(?:\.\d+)?)(ms|s|m|h)$/);
|
|
3
|
+
if (!match) {
|
|
4
|
+
throw new Error(`Invalid duration format: ${duration}`);
|
|
5
|
+
}
|
|
6
|
+
const value = parseFloat(match[1]);
|
|
7
|
+
const unit = match[2];
|
|
8
|
+
switch (unit) {
|
|
9
|
+
case 'ms':
|
|
10
|
+
return value;
|
|
11
|
+
case 's':
|
|
12
|
+
return value * 1000;
|
|
13
|
+
case 'm':
|
|
14
|
+
return value * 60 * 1000;
|
|
15
|
+
case 'h':
|
|
16
|
+
return value * 60 * 60 * 1000;
|
|
17
|
+
default:
|
|
18
|
+
throw new Error(`Unknown duration unit: ${unit}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { parseDuration } from './duration.js';
|
|
2
|
+
describe('parseDuration', () => {
|
|
3
|
+
it('should parse durations correctly', () => {
|
|
4
|
+
expect(parseDuration('100ms')).toBe(100);
|
|
5
|
+
expect(parseDuration('30s')).toBe(30000);
|
|
6
|
+
expect(parseDuration('5m')).toBe(300000);
|
|
7
|
+
expect(parseDuration('1h')).toBe(3600000);
|
|
8
|
+
});
|
|
9
|
+
it('should throw on invalid format', () => {
|
|
10
|
+
expect(() => parseDuration('invalid')).toThrow('Invalid duration format');
|
|
11
|
+
expect(() => parseDuration('10')).toThrow('Invalid duration format');
|
|
12
|
+
});
|
|
13
|
+
});
|
package/dist/lib/errors.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Centralized error handling for ARK CLI
|
|
3
3
|
*/
|
|
4
|
+
export declare const ExitCodes: {
|
|
5
|
+
readonly Success: 0;
|
|
6
|
+
readonly CliError: 1;
|
|
7
|
+
readonly OperationError: 2;
|
|
8
|
+
readonly Timeout: 3;
|
|
9
|
+
};
|
|
4
10
|
export declare enum ErrorCode {
|
|
5
11
|
INVALID_INPUT = "INVALID_INPUT",
|
|
6
12
|
FILE_NOT_FOUND = "FILE_NOT_FOUND",
|
package/dist/lib/errors.js
CHANGED
package/dist/lib/executeQuery.js
CHANGED
|
@@ -3,17 +3,24 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { execa } from 'execa';
|
|
5
5
|
import ora from 'ora';
|
|
6
|
+
import chalk from 'chalk';
|
|
6
7
|
import output from './output.js';
|
|
8
|
+
import { ExitCodes } from './errors.js';
|
|
9
|
+
import { parseDuration } from './duration.js';
|
|
7
10
|
/**
|
|
8
11
|
* Execute a query against any ARK target (model, agent, team)
|
|
9
12
|
* This is the shared implementation used by all query commands
|
|
10
13
|
*/
|
|
11
14
|
export async function executeQuery(options) {
|
|
12
15
|
const spinner = ora('Creating query...').start();
|
|
13
|
-
|
|
16
|
+
const queryTimeoutMs = options.timeout
|
|
17
|
+
? parseDuration(options.timeout)
|
|
18
|
+
: parseDuration('5m');
|
|
19
|
+
const watchTimeoutMs = options.watchTimeout
|
|
20
|
+
? parseDuration(options.watchTimeout)
|
|
21
|
+
: queryTimeoutMs + 60000;
|
|
14
22
|
const timestamp = Date.now();
|
|
15
23
|
const queryName = `cli-query-${timestamp}`;
|
|
16
|
-
// Create the Query resource
|
|
17
24
|
const queryManifest = {
|
|
18
25
|
apiVersion: 'ark.mckinsey.com/v1alpha1',
|
|
19
26
|
kind: 'Query',
|
|
@@ -22,6 +29,7 @@ export async function executeQuery(options) {
|
|
|
22
29
|
},
|
|
23
30
|
spec: {
|
|
24
31
|
input: options.message,
|
|
32
|
+
...(options.timeout && { timeout: options.timeout }),
|
|
25
33
|
targets: [
|
|
26
34
|
{
|
|
27
35
|
type: options.targetType,
|
|
@@ -37,86 +45,65 @@ export async function executeQuery(options) {
|
|
|
37
45
|
input: JSON.stringify(queryManifest),
|
|
38
46
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
39
47
|
});
|
|
40
|
-
// Watch for query completion
|
|
41
|
-
spinner.text = '
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const errorCondition = query.status?.conditions?.find((c) => {
|
|
73
|
-
return c.type === 'Complete' && c.status === 'False';
|
|
74
|
-
});
|
|
75
|
-
if (errorCondition?.message) {
|
|
76
|
-
output.error(errorCondition.message);
|
|
77
|
-
}
|
|
78
|
-
else if (query.status?.error) {
|
|
79
|
-
output.error(query.status.error);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
output.error('Query failed with unknown error');
|
|
83
|
-
}
|
|
48
|
+
// Watch for query completion using kubectl wait
|
|
49
|
+
spinner.text = 'Waiting for query completion...';
|
|
50
|
+
try {
|
|
51
|
+
await execa('kubectl', [
|
|
52
|
+
'wait',
|
|
53
|
+
'--for=condition=Completed',
|
|
54
|
+
`query/${queryName}`,
|
|
55
|
+
`--timeout=${Math.floor(watchTimeoutMs / 1000)}s`,
|
|
56
|
+
], { timeout: watchTimeoutMs });
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
spinner.stop();
|
|
60
|
+
// Check if it's a timeout or other error
|
|
61
|
+
if (error instanceof Error &&
|
|
62
|
+
error.message.includes('timed out waiting')) {
|
|
63
|
+
console.error(chalk.red(`Query did not complete within ${options.watchTimeout ?? `${Math.floor(watchTimeoutMs / 1000)}s`}`));
|
|
64
|
+
process.exit(ExitCodes.Timeout);
|
|
65
|
+
}
|
|
66
|
+
// For other errors, fetch the query to check status
|
|
67
|
+
}
|
|
68
|
+
spinner.stop();
|
|
69
|
+
// Fetch final query state
|
|
70
|
+
try {
|
|
71
|
+
const { stdout } = await execa('kubectl', ['get', 'query', queryName, '-o', 'json'], { stdio: 'pipe' });
|
|
72
|
+
const query = JSON.parse(stdout);
|
|
73
|
+
const phase = query.status?.phase;
|
|
74
|
+
// Check if query completed successfully or with error
|
|
75
|
+
if (phase === 'done') {
|
|
76
|
+
// Extract and display the response from responses array
|
|
77
|
+
if (query.status?.responses && query.status.responses.length > 0) {
|
|
78
|
+
const response = query.status.responses[0];
|
|
79
|
+
console.log(response.content || response);
|
|
84
80
|
}
|
|
85
|
-
else
|
|
86
|
-
|
|
87
|
-
spinner.warn('Query canceled');
|
|
88
|
-
// Try to get cancellation reason if available
|
|
89
|
-
if (query.status?.message) {
|
|
90
|
-
output.warning(query.status.message);
|
|
91
|
-
}
|
|
81
|
+
else {
|
|
82
|
+
output.warning('No response received');
|
|
92
83
|
}
|
|
93
84
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
85
|
+
else if (phase === 'error') {
|
|
86
|
+
const response = query.status?.responses?.[0];
|
|
87
|
+
console.error(chalk.red(response?.content || 'Query failed with unknown error'));
|
|
88
|
+
process.exit(ExitCodes.OperationError);
|
|
97
89
|
}
|
|
98
|
-
if (
|
|
99
|
-
|
|
90
|
+
else if (phase === 'canceled') {
|
|
91
|
+
spinner.warn('Query canceled');
|
|
92
|
+
if (query.status?.message) {
|
|
93
|
+
output.warning(query.status.message);
|
|
94
|
+
}
|
|
95
|
+
process.exit(ExitCodes.OperationError);
|
|
100
96
|
}
|
|
101
97
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.error(chalk.red(error instanceof Error ? error.message : 'Failed to fetch query result'));
|
|
100
|
+
process.exit(ExitCodes.CliError);
|
|
105
101
|
}
|
|
106
102
|
}
|
|
107
103
|
catch (error) {
|
|
108
|
-
spinner.
|
|
109
|
-
|
|
110
|
-
process.exit(
|
|
111
|
-
}
|
|
112
|
-
finally {
|
|
113
|
-
// Clean up the query resource
|
|
114
|
-
try {
|
|
115
|
-
await execa('kubectl', ['delete', 'query', queryName], { stdio: 'pipe' });
|
|
116
|
-
}
|
|
117
|
-
catch {
|
|
118
|
-
// Ignore cleanup errors
|
|
119
|
-
}
|
|
104
|
+
spinner.stop();
|
|
105
|
+
console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));
|
|
106
|
+
process.exit(ExitCodes.CliError);
|
|
120
107
|
}
|
|
121
108
|
}
|
|
122
109
|
/**
|
|
@@ -8,6 +8,7 @@ const mockSpinner = {
|
|
|
8
8
|
succeed: jest.fn(),
|
|
9
9
|
fail: jest.fn(),
|
|
10
10
|
warn: jest.fn(),
|
|
11
|
+
stop: jest.fn(),
|
|
11
12
|
text: '',
|
|
12
13
|
};
|
|
13
14
|
const mockOra = jest.fn(() => mockSpinner);
|
|
@@ -25,7 +26,11 @@ const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
|
|
|
25
26
|
throw new Error('process.exit called');
|
|
26
27
|
}));
|
|
27
28
|
const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
29
|
+
const mockConsoleError = jest
|
|
30
|
+
.spyOn(console, 'error')
|
|
31
|
+
.mockImplementation(() => { });
|
|
28
32
|
const { executeQuery, parseTarget } = await import('./executeQuery.js');
|
|
33
|
+
const { ExitCodes } = await import('./errors.js');
|
|
29
34
|
describe('executeQuery', () => {
|
|
30
35
|
beforeEach(() => {
|
|
31
36
|
jest.clearAllMocks();
|
|
@@ -71,9 +76,6 @@ describe('executeQuery', () => {
|
|
|
71
76
|
exitCode: 0,
|
|
72
77
|
};
|
|
73
78
|
}
|
|
74
|
-
if (args.includes('delete')) {
|
|
75
|
-
return { stdout: '', stderr: '', exitCode: 0 };
|
|
76
|
-
}
|
|
77
79
|
return { stdout: '', stderr: '', exitCode: 0 };
|
|
78
80
|
});
|
|
79
81
|
await executeQuery({
|
|
@@ -82,14 +84,14 @@ describe('executeQuery', () => {
|
|
|
82
84
|
message: 'Hello',
|
|
83
85
|
});
|
|
84
86
|
expect(mockSpinner.start).toHaveBeenCalled();
|
|
85
|
-
expect(mockSpinner.
|
|
86
|
-
expect(mockConsoleLog).toHaveBeenCalledWith('
|
|
87
|
+
expect(mockSpinner.stop).toHaveBeenCalled();
|
|
88
|
+
expect(mockConsoleLog).toHaveBeenCalledWith('Test response');
|
|
87
89
|
});
|
|
88
|
-
it('should handle query error phase', async () => {
|
|
90
|
+
it('should handle query error phase and exit with code 2', async () => {
|
|
89
91
|
const mockQueryResponse = {
|
|
90
92
|
status: {
|
|
91
93
|
phase: 'error',
|
|
92
|
-
|
|
94
|
+
responses: [{ content: 'Query failed with test error' }],
|
|
93
95
|
},
|
|
94
96
|
};
|
|
95
97
|
mockExeca.mockImplementation(async (command, args) => {
|
|
@@ -103,20 +105,23 @@ describe('executeQuery', () => {
|
|
|
103
105
|
exitCode: 0,
|
|
104
106
|
};
|
|
105
107
|
}
|
|
106
|
-
if (args.includes('delete')) {
|
|
107
|
-
return { stdout: '', stderr: '', exitCode: 0 };
|
|
108
|
-
}
|
|
109
108
|
return { stdout: '', stderr: '', exitCode: 0 };
|
|
110
109
|
});
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
try {
|
|
111
|
+
await executeQuery({
|
|
112
|
+
targetType: 'model',
|
|
113
|
+
targetName: 'default',
|
|
114
|
+
message: 'Hello',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
expect(error.message).toBe('process.exit called');
|
|
119
|
+
}
|
|
120
|
+
expect(mockSpinner.stop).toHaveBeenCalled();
|
|
121
|
+
expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Query failed with test error'));
|
|
122
|
+
expect(mockExit).toHaveBeenCalledWith(ExitCodes.OperationError);
|
|
118
123
|
});
|
|
119
|
-
it('should handle query canceled phase', async () => {
|
|
124
|
+
it('should handle query canceled phase and exit with code 2', async () => {
|
|
120
125
|
const mockQueryResponse = {
|
|
121
126
|
status: {
|
|
122
127
|
phase: 'canceled',
|
|
@@ -134,27 +139,27 @@ describe('executeQuery', () => {
|
|
|
134
139
|
exitCode: 0,
|
|
135
140
|
};
|
|
136
141
|
}
|
|
137
|
-
if (args.includes('delete')) {
|
|
138
|
-
return { stdout: '', stderr: '', exitCode: 0 };
|
|
139
|
-
}
|
|
140
142
|
return { stdout: '', stderr: '', exitCode: 0 };
|
|
141
143
|
});
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
try {
|
|
145
|
+
await executeQuery({
|
|
146
|
+
targetType: 'agent',
|
|
147
|
+
targetName: 'test-agent',
|
|
148
|
+
message: 'Hello',
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
expect(error.message).toBe('process.exit called');
|
|
153
|
+
}
|
|
147
154
|
expect(mockSpinner.warn).toHaveBeenCalledWith('Query canceled');
|
|
148
155
|
expect(mockOutput.warning).toHaveBeenCalledWith('Query was canceled');
|
|
156
|
+
expect(mockExit).toHaveBeenCalledWith(ExitCodes.OperationError);
|
|
149
157
|
});
|
|
150
|
-
it('should
|
|
158
|
+
it('should handle kubectl apply failures with exit code 1', async () => {
|
|
151
159
|
mockExeca.mockImplementation(async (command, args) => {
|
|
152
160
|
if (args.includes('apply')) {
|
|
153
161
|
throw new Error('Failed to apply');
|
|
154
162
|
}
|
|
155
|
-
if (args.includes('delete')) {
|
|
156
|
-
return { stdout: '', stderr: '', exitCode: 0 };
|
|
157
|
-
}
|
|
158
163
|
return { stdout: '', stderr: '', exitCode: 0 };
|
|
159
164
|
});
|
|
160
165
|
await expect(executeQuery({
|
|
@@ -162,9 +167,37 @@ describe('executeQuery', () => {
|
|
|
162
167
|
targetName: 'default',
|
|
163
168
|
message: 'Hello',
|
|
164
169
|
})).rejects.toThrow('process.exit called');
|
|
165
|
-
expect(mockSpinner.
|
|
166
|
-
expect(
|
|
167
|
-
expect(mockExit).toHaveBeenCalledWith(
|
|
170
|
+
expect(mockSpinner.stop).toHaveBeenCalled();
|
|
171
|
+
expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Failed to apply'));
|
|
172
|
+
expect(mockExit).toHaveBeenCalledWith(ExitCodes.CliError);
|
|
173
|
+
});
|
|
174
|
+
it('should handle query timeout and exit with code 3', async () => {
|
|
175
|
+
mockExeca.mockImplementation(async (command, args) => {
|
|
176
|
+
if (args.includes('apply')) {
|
|
177
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
178
|
+
}
|
|
179
|
+
if (args.includes('wait')) {
|
|
180
|
+
// Simulate kubectl wait timeout
|
|
181
|
+
const error = new Error('timed out waiting for the condition');
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
185
|
+
});
|
|
186
|
+
try {
|
|
187
|
+
await executeQuery({
|
|
188
|
+
targetType: 'model',
|
|
189
|
+
targetName: 'default',
|
|
190
|
+
message: 'Hello',
|
|
191
|
+
timeout: '100ms',
|
|
192
|
+
watchTimeout: '200ms',
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
expect(error.message).toBe('process.exit called');
|
|
197
|
+
}
|
|
198
|
+
expect(mockSpinner.stop).toHaveBeenCalled();
|
|
199
|
+
expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Query did not complete within 200ms'));
|
|
200
|
+
expect(mockExit).toHaveBeenCalledWith(ExitCodes.Timeout);
|
|
168
201
|
});
|
|
169
202
|
});
|
|
170
203
|
});
|
package/package.json
CHANGED
|
@@ -62,6 +62,7 @@ kubectl get queries
|
|
|
62
62
|
| `image.repository` | string | `"{{ .Values.mcpServerName }}"` | Image repository |
|
|
63
63
|
| `image.tag` | string | `"latest"` | Image tag |
|
|
64
64
|
| `service.port` | int | `8080` | Service port |
|
|
65
|
+
| `mcpServer.timeout` | string | `"30s"` | Timeout for MCP tool calls (e.g., "5m", "10m") |
|
|
65
66
|
|
|
66
67
|
{{- if .Values.requiresAuth }}
|
|
67
68
|
| `auth.token` | string | `""` | Authentication token |
|
|
@@ -15,6 +15,9 @@ spec:
|
|
|
15
15
|
path: {{ .Values.mcpServer.path }}
|
|
16
16
|
{{- end }}
|
|
17
17
|
transport: sse
|
|
18
|
+
{{- if .Values.mcpServer.timeout }}
|
|
19
|
+
timeout: {{ .Values.mcpServer.timeout | quote }}
|
|
20
|
+
{{- end }}
|
|
18
21
|
{{- if .Values.mcpServer.description }}
|
|
19
22
|
description: {{ .Values.mcpServer.description | quote }}
|
|
20
23
|
{{- end }}
|