Skip to content

Commit 35ee3b8

Browse files
authored
feat: add target_port to run custom server (#501)
1 parent 6f2803e commit 35ee3b8

File tree

5 files changed

+47
-0
lines changed

5 files changed

+47
-0
lines changed

renderer/src/features/mcp-servers/components/__tests__/dialog-form-run-mcp-command.test.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ it('is able to run an MCP server with docker', async () => {
8989
await userEvent.click(screen.getByLabelText('Transport'))
9090
await userEvent.click(screen.getByRole('option', { name: 'stdio' }))
9191

92+
await userEvent.type(screen.getByLabelText('Target port'), '8000')
93+
9294
await userEvent.type(
9395
screen.getByLabelText('Docker image'),
9496
'ghcr.io/github/github-mcp-server'
@@ -139,6 +141,7 @@ it('is able to run an MCP server with docker', async () => {
139141
expect(payload).toBeDefined()
140142
expect(payload['name'], 'Should have name').toBe('foo-bar')
141143
expect(payload['transport'], 'Should have transport').toBe('stdio')
144+
expect(payload['target_port']).toBe(8000)
142145
expect(payload['image'], 'Should have image').toBe(
143146
'ghcr.io/github/github-mcp-server'
144147
)
@@ -189,6 +192,8 @@ it('is able to run an MCP server with npx', async () => {
189192
await userEvent.click(screen.getByLabelText('Transport'))
190193
await userEvent.click(screen.getByRole('option', { name: 'stdio' }))
191194

195+
await userEvent.type(screen.getByLabelText('Target port'), '8800')
196+
192197
await userEvent.click(screen.getByLabelText('Protocol'))
193198
await userEvent.click(screen.getByRole('option', { name: 'npx' }))
194199

@@ -243,6 +248,7 @@ it('is able to run an MCP server with npx', async () => {
243248
expect(payload['name'], 'Should have name').toBe('foo-bar')
244249
expect(payload['transport'], 'Should have transport').toBe('stdio')
245250
expect(payload['protocol'], 'Should have protocol').toBe('npx')
251+
expect(payload['target_port']).toBe(8800)
246252
expect(payload['package_name'], 'Should have package name').toBe(
247253
'@modelcontextprotocol/server-everything'
248254
)
@@ -293,6 +299,8 @@ it('is able to run an MCP server with uvx', async () => {
293299
await userEvent.click(screen.getByLabelText('Transport'))
294300
await userEvent.click(screen.getByRole('option', { name: 'stdio' }))
295301

302+
await userEvent.type(screen.getByLabelText('Target port'), '8000')
303+
296304
await userEvent.click(screen.getByLabelText('Protocol'))
297305
await userEvent.click(screen.getByRole('option', { name: 'uvx' }))
298306

@@ -347,6 +355,7 @@ it('is able to run an MCP server with uvx', async () => {
347355
expect(payload['name'], 'Should have name').toBe('foo-bar')
348356
expect(payload['transport'], 'Should have transport').toBe('stdio')
349357
expect(payload['protocol'], 'Should have protocol').toBe('uvx')
358+
expect(payload['target_port']).toBe(8000)
350359
expect(payload['package_name'], 'Should have package name').toBe(
351360
'mcp-server-fetch'
352361
)
@@ -397,6 +406,8 @@ it('is able to run an MCP server with go', async () => {
397406
await userEvent.click(screen.getByLabelText('Transport'))
398407
await userEvent.click(screen.getByRole('option', { name: 'stdio' }))
399408

409+
await userEvent.type(screen.getByLabelText('Target port'), '8000')
410+
400411
await userEvent.click(screen.getByLabelText('Protocol'))
401412
await userEvent.click(screen.getByRole('option', { name: 'go' }))
402413

@@ -451,6 +462,7 @@ it('is able to run an MCP server with go', async () => {
451462
expect(payload['name'], 'Should have name').toBe('foo-bar')
452463
expect(payload['transport'], 'Should have transport').toBe('stdio')
453464
expect(payload['protocol'], 'Should have protocol').toBe('go')
465+
expect(payload['target_port']).toBe(8000)
454466
expect(payload['package_name'], 'Should have package name').toBe(
455467
'github.com/example/go-mcp-server'
456468
)

renderer/src/features/mcp-servers/components/dialog-form-run-mcp-command.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export function DialogFormRunMcpServerWithCommand({
4040
resolver: zodV4Resolver(getFormSchemaRunMcpCommand(workloads)),
4141
defaultValues: {
4242
type: 'docker_image',
43+
target_port: undefined,
4344
},
4445
})
4546

renderer/src/features/mcp-servers/components/form-fields-run-mcp-command.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,37 @@ export function FormFieldsRunMcpCommand({
109109
)}
110110
/>
111111

112+
<FormField
113+
control={form.control}
114+
name="target_port"
115+
render={({ field }) => (
116+
<FormItem>
117+
<div className="flex items-center gap-1">
118+
<FormLabel htmlFor={field.name}>Target port</FormLabel>
119+
<TooltipInfoIcon>
120+
Target port to expose from the container. If not specified,
121+
ToolHive will automatically assign a random port.
122+
</TooltipInfoIcon>
123+
</div>
124+
<FormControl>
125+
<Input
126+
id={field.name}
127+
autoCorrect="off"
128+
autoComplete="off"
129+
autoFocus
130+
type="number"
131+
data-1p-ignore
132+
placeholder="e.g. 50051"
133+
defaultValue={field.value}
134+
onChange={(e) => field.onChange(parseInt(e.target.value, 10))}
135+
name={field.name}
136+
/>
137+
</FormControl>
138+
<FormMessage />
139+
</FormItem>
140+
)}
141+
/>
142+
112143
{typeValue === 'docker_image' ? (
113144
<FormField
114145
control={form.control}

renderer/src/features/mcp-servers/lib/form-schema-run-mcp-server-with-command.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const getCommonFields = (workloads: WorkloadsWorkload[]) =>
1414
[z.literal('sse'), z.literal('stdio'), z.literal('streamable-http')],
1515
'Please select either SSE, stdio, or streamable-http.'
1616
),
17+
target_port: z.number().optional(),
1718
cmd_arguments: z.string().optional(),
1819
envVars: z
1920
.object({

renderer/src/features/mcp-servers/lib/orchestrate-run-custom-server.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,15 @@ function transformTypeSpecificData(
134134
name: values.name,
135135
transport: values.transport,
136136
image: values.image,
137+
target_port: values.target_port,
137138
}
138139
}
139140
case 'package_manager': {
140141
return {
141142
name: values.name,
142143
transport: values.transport,
143144
image: `${values.protocol}://${values.package_name}`,
145+
target_port: values.target_port,
144146
}
145147
}
146148
default:

0 commit comments

Comments
 (0)