-
Notifications
You must be signed in to change notification settings - Fork 1
allow setting options at mcp install time #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -157,7 +157,25 @@ describe('PackageService python runtime support', () => { | |
| runtime: 'python', | ||
| pythonModule: 'my_mcp_server', | ||
| pythonArgs: ['--port', '0'], | ||
| env: { PATH: '/usr/bin', CUSTOM_ENV: '1' } | ||
| env: { PATH: '/usr/bin', CUSTOM_ENV: '1' }, | ||
| displayName: 'Python Server', | ||
| description: 'Python MCP test server', | ||
| secretNames: ['apiKey'], | ||
| secretFields: [ | ||
| { | ||
| name: 'apiKey', | ||
| label: 'API key', | ||
| description: 'API key used by the package.', | ||
| required: true, | ||
| inputType: 'password' | ||
| } | ||
| ], | ||
| homepageUrl: 'https://example.com/python-server', | ||
| repositoryUrl: 'https://github.com/example/python-server', | ||
| licenseName: 'MIT', | ||
| catalogProvider: 'manual', | ||
| catalogId: 'python-server', | ||
| startupTimeout: 600000 | ||
| }) | ||
|
|
||
| expect(result.success).toBe(true) | ||
|
|
@@ -173,6 +191,16 @@ describe('PackageService python runtime support', () => { | |
| command: string | ||
| args: string[] | ||
| env: Record<string, string> | ||
| displayName: string | ||
| description: string | ||
| secretNames: string[] | ||
| secretFields: Array<{ name: string; label: string; description: string; required: boolean; inputType: string }> | ||
| homepageUrl: string | ||
| repositoryUrl: string | ||
| licenseName: string | ||
| catalogProvider: string | ||
| catalogId: string | ||
| startupTimeout: number | ||
| } | ||
| const expectedVenvAbsolutePath = path.resolve(process.cwd(), path.join('packages/python', 'python-server')) | ||
| const expectedPythonCommand = path.join( | ||
|
|
@@ -190,6 +218,133 @@ describe('PackageService python runtime support', () => { | |
| path.join(expectedVenvAbsolutePath, process.platform === 'win32' ? 'Scripts' : 'bin') | ||
| ) | ||
| ).toBe(true) | ||
| expect(addServerInput.displayName).toBe('Python Server') | ||
| expect(addServerInput.description).toBe('Python MCP test server') | ||
| expect(addServerInput.secretNames).toEqual(['apiKey']) | ||
| expect(addServerInput.secretFields).toEqual([ | ||
| { | ||
| name: 'apiKey', | ||
| label: 'API key', | ||
| description: 'API key used by the package.', | ||
| required: true, | ||
| inputType: 'password' | ||
| } | ||
| ]) | ||
| expect(addServerInput.homepageUrl).toBe('https://example.com/python-server') | ||
| expect(addServerInput.repositoryUrl).toBe('https://github.com/example/python-server') | ||
| expect(addServerInput.licenseName).toBe('MIT') | ||
| expect(addServerInput.catalogProvider).toBe('manual') | ||
| expect(addServerInput.catalogId).toBe('python-server') | ||
| expect(addServerInput.startupTimeout).toBe(600000) | ||
| }) | ||
|
|
||
| test('installPackage forwards server metadata for node stdio packages', async () => { | ||
| const { service, mcpMock } = createService() | ||
|
|
||
| mcpMock.addServer.mockResolvedValue({ | ||
| name: 'node-server', | ||
| transportType: 'stdio', | ||
|
Comment on lines
+241
to
+246
|
||
| command: 'node', | ||
| args: [], | ||
| env: {}, | ||
| status: 'disconnected', | ||
| enabled: true | ||
| }) | ||
|
|
||
| const result = await service.installPackage({ | ||
| name: '@example/node-mcp', | ||
| serverName: 'node-server', | ||
| command: 'node', | ||
| args: ['./server.js'], | ||
| secretNames: ['token'], | ||
| secretFields: [ | ||
| { | ||
| name: 'token', | ||
| label: 'Token', | ||
| description: 'Token used by the package.', | ||
| required: true, | ||
| inputType: 'password' | ||
| } | ||
| ], | ||
| homepageUrl: 'https://example.com/node-mcp', | ||
| repositoryUrl: 'https://github.com/example/node-mcp', | ||
| startupTimeout: 300000 | ||
| }) | ||
|
|
||
| expect(result.success).toBe(true) | ||
| expect(mcpMock.addServer).toHaveBeenCalledWith( | ||
| expect.objectContaining({ | ||
| name: 'node-server', | ||
| transportType: 'stdio', | ||
| command: 'node', | ||
| args: ['./server.js'], | ||
| secretNames: ['token'], | ||
| secretFields: [ | ||
| { | ||
| name: 'token', | ||
| label: 'Token', | ||
| description: 'Token used by the package.', | ||
| required: true, | ||
| inputType: 'password' | ||
| } | ||
| ], | ||
| homepageUrl: 'https://example.com/node-mcp', | ||
| repositoryUrl: 'https://github.com/example/node-mcp', | ||
| startupTimeout: 300000 | ||
| }) | ||
| ) | ||
| }) | ||
|
|
||
| test('installPackage forwards server metadata for streamable HTTP packages', async () => { | ||
| const { service, mcpMock } = createService() | ||
|
|
||
| mcpMock.addServer.mockResolvedValue({ | ||
| name: 'remote-server', | ||
| transportType: 'streamable_http', | ||
| url: 'https://mcp.example.com/mcp', | ||
| status: 'disconnected', | ||
| enabled: true | ||
| }) | ||
|
|
||
| const result = await service.installPackage({ | ||
| name: '@example/remote-mcp', | ||
| serverName: 'remote-server', | ||
| transportType: 'streamable_http', | ||
| url: 'https://mcp.example.com/mcp', | ||
| displayName: 'Remote MCP', | ||
| secretNames: ['bearerToken'], | ||
| secretFields: [ | ||
| { | ||
| name: 'bearerToken', | ||
| label: 'Bearer token', | ||
| description: 'Bearer token used by the package.', | ||
| required: true, | ||
| inputType: 'password' | ||
| } | ||
| ], | ||
| startupTimeout: 900000 | ||
| }) | ||
|
|
||
| expect(result.success).toBe(true) | ||
| expect(mcpMock.addServer).toHaveBeenCalledWith( | ||
| expect.objectContaining({ | ||
| name: 'remote-server', | ||
| transportType: 'streamable_http', | ||
| url: 'https://mcp.example.com/mcp', | ||
| displayName: 'Remote MCP', | ||
| secretNames: ['bearerToken'], | ||
| secretFields: [ | ||
| { | ||
| name: 'bearerToken', | ||
| label: 'Bearer token', | ||
| description: 'Bearer token used by the package.', | ||
| required: true, | ||
| inputType: 'password' | ||
| } | ||
| ], | ||
| startupTimeout: 900000 | ||
| }) | ||
| ) | ||
| }) | ||
|
|
||
| test('upgradePackage uses pip for python runtime and does not update server command/args', async () => { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
installPackagenow forwards user-providedsecretNames/secretFields(and other metadata) intoMCPService.addServer, but for platform serversaddServerdoesn't apply the external-server validations (max secret fields, field name pattern, etc.). Since this input comes from the install request, it should be validated/normalized inPackageService(e.g., cap the number of secret fields and enforce the samefield.namepattern used inMCPService.addServer) before persisting it on the server record.