diff --git a/api-docs/openapi/v3_0/mcpV1alpha1Api.json b/api-docs/openapi/v3_0/mcpV1alpha1Api.json index 3322ce9..09a84d6 100644 --- a/api-docs/openapi/v3_0/mcpV1alpha1Api.json +++ b/api-docs/openapi/v3_0/mcpV1alpha1Api.json @@ -287,8 +287,10 @@ "allowedTools" : { "uniqueItems" : true, "type" : "array", + "description" : "Allowed MCP tool names. Use '*' to automatically allow all current and future tools.", "items" : { - "type" : "string" + "type" : "string", + "description" : "Allowed MCP tool names. Use '*' to automatically allow all current and future tools." } }, "displayName" : { @@ -349,8 +351,10 @@ "allowedTools" : { "uniqueItems" : true, "type" : "array", + "description" : "Allowed MCP tool names. Use '*' to automatically allow all current and future tools.", "items" : { - "type" : "string" + "type" : "string", + "description" : "Allowed MCP tool names. Use '*' to automatically allow all current and future tools." } }, "creationTimestamp" : { @@ -616,8 +620,10 @@ "allowedTools" : { "uniqueItems" : true, "type" : "array", + "description" : "Allowed MCP tool names. Use '*' to automatically allow all current and future tools.", "items" : { - "type" : "string" + "type" : "string", + "description" : "Allowed MCP tool names. Use '*' to automatically allow all current and future tools." } }, "displayName" : { diff --git a/src/main/java/run/halo/mcpserver/AuthorizedMcpTransport.java b/src/main/java/run/halo/mcpserver/AuthorizedMcpTransport.java index 5353fa2..bdcfe80 100644 --- a/src/main/java/run/halo/mcpserver/AuthorizedMcpTransport.java +++ b/src/main/java/run/halo/mcpserver/AuthorizedMcpTransport.java @@ -69,10 +69,10 @@ public Mono handleNotification( } private Mono listTools(McpSchema.JSONRPCRequest request) { - return authorization.allowedTools() + return authorization.authentication() .zipWith(catalog.protocolTools()) .map(tuple -> tuple.getT2().stream() - .filter(tool -> tuple.getT1().contains(tool.name())) + .filter(tool -> tuple.getT1().allows(tool.name())) .toList()) .map(tools -> McpSchema.JSONRPCResponse.result( request.id(), McpSchema.ListToolsResult.builder(tools).build())) @@ -94,14 +94,16 @@ private Mono callTool( () -> Mono.just(protocolError(request, -32602, "Invalid tools/call parameters"))); } var toolName = call.name() == null ? "" : call.name(); - return recentCallHistory.observe( + var rateLimitTool = authentication.allowsAllTools() + ? catalog.hasProtocolTool(toolName) + .map(available -> available ? toolName : "") + : Mono.just(authentication.allows(toolName) ? toolName : ""); + return rateLimitTool.flatMap(name -> recentCallHistory.observe( authentication, toolName, - () -> rateLimiter.allowTool( - authentication.keyId(), - authentication.allows(toolName) ? toolName : "") + () -> rateLimiter.allowTool(authentication.keyId(), name) ? executeTool(context, request, handler, call, toolName) - : Mono.just(rateLimited(request))); + : Mono.just(rateLimited(request)))); }); } diff --git a/src/main/java/run/halo/mcpserver/McpAccessKey.java b/src/main/java/run/halo/mcpserver/McpAccessKey.java index bfc6059..15880e6 100644 --- a/src/main/java/run/halo/mcpserver/McpAccessKey.java +++ b/src/main/java/run/halo/mcpserver/McpAccessKey.java @@ -19,6 +19,9 @@ singular = "mcpaccesskey") public class McpAccessKey extends AbstractExtension { + static final String ALLOWED_TOOLS_DESCRIPTION = + "Allowed MCP tool names. Use '*' to automatically allow all current and future tools."; + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) private Spec spec = new Spec(); @@ -33,6 +36,7 @@ public static class Spec { private String ownerName; private boolean enabled = true; private Instant expiresAt; + @Schema(description = ALLOWED_TOOLS_DESCRIPTION) private Set allowedTools = new LinkedHashSet<>(); @Schema(description = "Allowed IPv4/IPv6 addresses or CIDR ranges. Empty means unrestricted.") private Set allowedIpRanges = new LinkedHashSet<>(); diff --git a/src/main/java/run/halo/mcpserver/McpAccessKeyEndpoint.java b/src/main/java/run/halo/mcpserver/McpAccessKeyEndpoint.java index c979959..8b709cd 100644 --- a/src/main/java/run/halo/mcpserver/McpAccessKeyEndpoint.java +++ b/src/main/java/run/halo/mcpserver/McpAccessKeyEndpoint.java @@ -255,6 +255,7 @@ private Mono validateTools(Set requestedTools, Set allowed var requested = tools(requestedTools); return toolCatalog.availableNames().flatMap(available -> { var unknown = new LinkedHashSet<>(requested); + unknown.remove(McpKeyAuthenticationToken.ALL_TOOLS); unknown.removeAll(available); unknown.removeAll(allowedUnavailableTools); if (!unknown.isEmpty()) { @@ -302,7 +303,10 @@ public GroupVersion groupVersion() { @Schema(name = "CreateMcpAccessKeyRequest") record CreateKeyRequest( @Schema(requiredMode = Schema.RequiredMode.REQUIRED) String displayName, - @Schema(requiredMode = Schema.RequiredMode.REQUIRED) Set allowedTools, + @Schema( + requiredMode = Schema.RequiredMode.REQUIRED, + description = McpAccessKey.ALLOWED_TOOLS_DESCRIPTION) + Set allowedTools, @Schema( requiredMode = Schema.RequiredMode.REQUIRED, description = "Allowed IPv4/IPv6 addresses or CIDR ranges. Empty means unrestricted.") @@ -312,7 +316,10 @@ record CreateKeyRequest( @Schema(name = "UpdateMcpAccessKeyRequest") record UpdateKeyRequest( @Schema(requiredMode = Schema.RequiredMode.REQUIRED) String displayName, - @Schema(requiredMode = Schema.RequiredMode.REQUIRED) Set allowedTools, + @Schema( + requiredMode = Schema.RequiredMode.REQUIRED, + description = McpAccessKey.ALLOWED_TOOLS_DESCRIPTION) + Set allowedTools, @Schema( requiredMode = Schema.RequiredMode.REQUIRED, description = "Allowed IPv4/IPv6 addresses or CIDR ranges. Empty means unrestricted.") @@ -328,7 +335,10 @@ record AccessKeyView( @Schema(requiredMode = Schema.RequiredMode.REQUIRED) String ownerName, @Schema(requiredMode = Schema.RequiredMode.REQUIRED) boolean enabled, Instant expiresAt, - @Schema(requiredMode = Schema.RequiredMode.REQUIRED) Set allowedTools, + @Schema( + requiredMode = Schema.RequiredMode.REQUIRED, + description = McpAccessKey.ALLOWED_TOOLS_DESCRIPTION) + Set allowedTools, @Schema( requiredMode = Schema.RequiredMode.REQUIRED, description = "Allowed IPv4/IPv6 addresses or CIDR ranges. Empty means unrestricted.") diff --git a/src/main/java/run/halo/mcpserver/McpAuthorization.java b/src/main/java/run/halo/mcpserver/McpAuthorization.java index e6e374b..f5e438c 100644 --- a/src/main/java/run/halo/mcpserver/McpAuthorization.java +++ b/src/main/java/run/halo/mcpserver/McpAuthorization.java @@ -1,6 +1,5 @@ package run.halo.mcpserver; -import java.util.Set; import java.util.function.Supplier; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.SecurityContext; @@ -48,8 +47,4 @@ Mono authentication() { public Mono username() { return authentication().map(McpKeyAuthenticationToken::getName); } - - Mono> allowedTools() { - return authentication().map(McpKeyAuthenticationToken::allowedTools); - } } diff --git a/src/main/java/run/halo/mcpserver/McpKeyAuthenticationToken.java b/src/main/java/run/halo/mcpserver/McpKeyAuthenticationToken.java index fa92176..3d3d3b3 100644 --- a/src/main/java/run/halo/mcpserver/McpKeyAuthenticationToken.java +++ b/src/main/java/run/halo/mcpserver/McpKeyAuthenticationToken.java @@ -7,6 +7,8 @@ final class McpKeyAuthenticationToken extends AbstractAuthenticationToken { + static final String ALL_TOOLS = "*"; + private final String keyId; private final String keyDisplayName; private final String keyPrefix; @@ -40,12 +42,12 @@ String keyPrefix() { return keyPrefix; } - Set allowedTools() { - return allowedTools; + boolean allowsAllTools() { + return allowedTools.contains(ALL_TOOLS); } boolean allows(String toolName) { - return allowedTools.contains(toolName); + return allowsAllTools() || allowedTools.contains(toolName); } @Override diff --git a/src/main/java/run/halo/mcpserver/McpToolCatalog.java b/src/main/java/run/halo/mcpserver/McpToolCatalog.java index 94e4ff9..1e7f9f7 100644 --- a/src/main/java/run/halo/mcpserver/McpToolCatalog.java +++ b/src/main/java/run/halo/mcpserver/McpToolCatalog.java @@ -63,6 +63,14 @@ Mono> protocolTools() { }); } + Mono hasProtocolTool(String name) { + if (builtInTools.tools().stream().anyMatch(tool -> tool.protocolTool().name().equals(name))) { + return Mono.just(true); + } + return contributedTools().map(tools -> + tools.stream().anyMatch(tool -> tool.protocolName().equals(name))); + } + private Mono> contributedTools() { return toolRegistry.registeredTools().onErrorResume(error -> { log.warn("Ignoring unavailable contributed MCP tools", error); diff --git a/src/test/java/run/halo/mcpserver/AuthorizedMcpTransportTest.java b/src/test/java/run/halo/mcpserver/AuthorizedMcpTransportTest.java index 00baaaa..0f19e11 100644 --- a/src/test/java/run/halo/mcpserver/AuthorizedMcpTransportTest.java +++ b/src/test/java/run/halo/mcpserver/AuthorizedMcpTransportTest.java @@ -4,6 +4,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -11,6 +12,7 @@ import io.modelcontextprotocol.json.jackson3.JacksonMcpJsonMapper; import io.modelcontextprotocol.server.McpStatelessServerHandler; import io.modelcontextprotocol.spec.McpSchema; +import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; @@ -51,6 +53,50 @@ void sanitizesInternalErrorsFromBuiltInToolCalls() { assertInternalErrorIsSanitized(response, 2); } + @Test + void wildcardListsEveryCurrentlyAvailableTool() { + var fixture = fixture(); + var first = McpSchema.Tool.builder("halo_first", Map.of("type", "object")).build(); + var addedLater = McpSchema.Tool.builder("PluginExample__added_later", Map.of("type", "object")) + .build(); + when(fixture.catalog().protocolTools()).thenReturn(Mono.just(List.of(first, addedLater))); + var request = new McpSchema.JSONRPCRequest("tools/list", 3, Map.of()); + var authentication = new McpKeyAuthenticationToken( + "key-id", "Automation", "hmcp_key", "admin", Set.of("*")); + + var response = fixture.handler() + .handleRequest(McpTransportContext.EMPTY, request) + .contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication)) + .block(); + + assertThat(response.result()).isInstanceOf(McpSchema.ListToolsResult.class); + var result = (McpSchema.ListToolsResult) response.result(); + assertThat(result.tools()).extracting(McpSchema.Tool::name) + .containsExactly("halo_first", "PluginExample__added_later"); + } + + @Test + void wildcardCallsKeepAvailableToolBucketsAndShareTheUnauthorizedBucket() { + var fixture = fixture(); + when(fixture.catalog().hasProtocolTool(any())).thenAnswer(invocation -> Mono.just( + Set.of("known_one", "known_two").contains(invocation.getArgument(0)))); + var authentication = new McpKeyAuthenticationToken( + "key-id", "Automation", "hmcp_key", "admin", Set.of("*")); + + for (var toolName : List.of("known_one", "known_two", "missing_one", "missing_two")) { + var request = new McpSchema.JSONRPCRequest( + "tools/call", 4, Map.of("name", toolName, "arguments", Map.of())); + fixture.handler() + .handleRequest(McpTransportContext.EMPTY, request) + .contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication)) + .block(); + } + + verify(fixture.rateLimiter()).allowTool("key-id", "known_one"); + verify(fixture.rateLimiter()).allowTool("key-id", "known_two"); + verify(fixture.rateLimiter(), times(2)).allowTool("key-id", ""); + } + @Test void acceptsInitializedNotificationWithoutDelegatingToMissingSdkHandler() { var fixture = fixture(); @@ -79,6 +125,8 @@ private static Fixture fixture() { var delegate = mock(WebFluxStatelessServerTransport.class); var catalog = mock(McpToolCatalog.class); var registry = mock(McpToolRegistry.class); + var rateLimiter = mock(McpRequestRateLimiter.class); + when(rateLimiter.allowTool(any(), any())).thenReturn(true); var authorization = new McpAuthorization(); var transport = new AuthorizedMcpTransport( delegate, @@ -86,7 +134,7 @@ private static Fixture fixture() { catalog, registry, authorization, - new McpRequestRateLimiter(), + rateLimiter, new McpRecentCallHistory()); var sdkHandler = mock(McpStatelessServerHandler.class); when(sdkHandler.handleNotification(any(), any())).thenReturn(Mono.empty()); @@ -99,7 +147,7 @@ private static Fixture fixture() { transport.setMcpHandler(sdkHandler); var captor = ArgumentCaptor.forClass(McpStatelessServerHandler.class); verify(delegate).setMcpHandler(captor.capture()); - return new Fixture(captor.getValue(), sdkHandler); + return new Fixture(captor.getValue(), sdkHandler, catalog, rateLimiter); } private static void assertInternalErrorIsSanitized( @@ -112,5 +160,9 @@ private static void assertInternalErrorIsSanitized( assertThat(response.toString()).doesNotContain("database password"); } - private record Fixture(McpStatelessServerHandler handler, McpStatelessServerHandler sdkHandler) {} + private record Fixture( + McpStatelessServerHandler handler, + McpStatelessServerHandler sdkHandler, + McpToolCatalog catalog, + McpRequestRateLimiter rateLimiter) {} } diff --git a/src/test/java/run/halo/mcpserver/HaloMcpServerTest.java b/src/test/java/run/halo/mcpserver/HaloMcpServerTest.java index cde9c3f..1455c6f 100644 --- a/src/test/java/run/halo/mcpserver/HaloMcpServerTest.java +++ b/src/test/java/run/halo/mcpserver/HaloMcpServerTest.java @@ -53,15 +53,16 @@ class HaloMcpServerTest { McpRecentCallHistory recentCallHistory; WebTestClient client; McpRequestRateLimiter rateLimiter; + McpAuthorization authorization; @BeforeEach void setUp() { when(pluginContext.getVersion()).thenReturn("1.0.0"); lenient().when(extensionGetter.getEnabledExtensions(run.halo.mcpserver.api.McpToolProvider.class)) .thenReturn(Flux.empty()); - var authorization = new McpAuthorization(); - var searchTool = builtInTool("halo_search_content", "Search content"); - var getPostTool = builtInTool("halo_get_post", "Read post"); + authorization = new McpAuthorization(); + var searchTool = builtInTool("halo_search_content", "Search content", authorization); + var getPostTool = builtInTool("halo_get_post", "Read post", authorization); lenient().when(builtInTools.tools()).thenReturn(java.util.List.of(searchTool, getPostTool)); when(builtInTools.specifications()).thenReturn(java.util.List.of( searchTool.specification(), getPostTool.specification())); @@ -313,6 +314,66 @@ void listsAndCallsAContributedToolDirectly() { .isEqualTo(McpToolSourceType.PLUGIN); } + @Test + void wildcardCallsBuiltInAndContributedTools() { + var definition = McpToolDefinition.builder() + .name("hello") + .title("Hello") + .description("Returns a greeting") + .inputSchema(java.util.Map.of("type", "object")) + .permission(invocation -> Mono.just(true)) + .handler(invocation -> Mono.just(McpToolResult.success( + java.util.Map.of("message", "Hello")))) + .build(); + when(extensionGetter.getEnabledExtensions(McpToolProvider.class)).thenReturn(Flux.just(provider)); + when(provider.tools()).thenReturn(Flux.just(definition)); + var plugin = mock(PluginWrapper.class); + when(plugin.getPluginId()).thenReturn("demo"); + when(pluginManager.whichPlugin(AopUtils.getTargetClass(provider))).thenReturn(plugin); + var authentication = new McpKeyAuthenticationToken( + "wildcard-key", "Automation", "hmcp_key", "admin", java.util.Set.of("*")); + var wildcardClient = WebTestClient.bindToRouterFunction(server.routerFunction()) + .webFilter((exchange, chain) -> chain.filter(exchange) + .contextWrite(org.springframework.security.core.context.ReactiveSecurityContextHolder + .withAuthentication(authentication))) + .build(); + + wildcardClient.post() + .uri("/mcp") + .contentType(MediaType.APPLICATION_JSON) + .header(HttpHeaders.ACCEPT, "application/json, text/event-stream") + .bodyValue(""" + { + "jsonrpc":"2.0", + "id":11, + "method":"tools/call", + "params":{"name":"halo_get_post","arguments":{"marker":"built-in"}} + } + """) + .exchange() + .expectStatus().isOk() + .expectBody() + .jsonPath("$.result.structuredContent.marker").isEqualTo("built-in"); + + wildcardClient.post() + .uri("/mcp") + .contentType(MediaType.APPLICATION_JSON) + .header(HttpHeaders.ACCEPT, "application/json, text/event-stream") + .bodyValue(""" + { + "jsonrpc":"2.0", + "id":12, + "method":"tools/call", + "params":{"name":"demo__hello","arguments":{}} + } + """) + .exchange() + .expectStatus().isOk() + .expectBody() + .jsonPath("$.result.structuredContent.message").isEqualTo("Hello") + .jsonPath("$.result.isError").isEqualTo(false); + } + @Test void getIsNotSupportedAndOtherPathsAreNotExposed() { client.get().uri("/mcp").exchange().expectStatus().isEqualTo(405); @@ -367,7 +428,8 @@ void keepsBuiltInToolsWhenProviderDiscoveryFails() { .assertThat(body.toString()).doesNotContain("storage password")); } - private static BuiltInTool builtInTool(String name, String title) { + private static BuiltInTool builtInTool( + String name, String title, McpAuthorization authorization) { var tool = io.modelcontextprotocol.spec.McpSchema.Tool.builder( name, java.util.Map.of("type", "object", "properties", java.util.Map.of())) @@ -382,10 +444,10 @@ private static BuiltInTool builtInTool(String name, String title) { .build(); var specification = io.modelcontextprotocol.server.McpStatelessServerFeatures.AsyncToolSpecification.builder() .tool(tool) - .callHandler((context, request) -> Mono.just( + .callHandler((context, request) -> authorization.authorize(name, () -> Mono.just( io.modelcontextprotocol.spec.McpSchema.CallToolResult.builder() .structuredContent(request.arguments()) - .build())) + .build()))) .build(); return new BuiltInTool(specification, "TEST", title, title); } diff --git a/src/test/java/run/halo/mcpserver/McpAccessKeyEndpointTest.java b/src/test/java/run/halo/mcpserver/McpAccessKeyEndpointTest.java index b07f37d..feac8d4 100644 --- a/src/test/java/run/halo/mcpserver/McpAccessKeyEndpointTest.java +++ b/src/test/java/run/halo/mcpserver/McpAccessKeyEndpointTest.java @@ -13,6 +13,8 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.test.web.reactive.server.WebTestClient; import reactor.core.publisher.Flux; import run.halo.app.extension.Metadata; @@ -139,6 +141,83 @@ void preservesExistingUnavailableToolsButRejectsNewUnknownTools() { .isBadRequest(); } + @Test + void acceptsTheAllToolsWildcard() { + when(toolCatalog.availableNames()).thenReturn(reactor.core.publisher.Mono.just(Set.of())); + when(accessKeyService.allowedTools("test-key")) + .thenReturn(reactor.core.publisher.Mono.just(Set.of())); + var updated = new McpAccessKey(); + updated.setMetadata(new Metadata()); + updated.getMetadata().setName("test-key"); + updated.getSpec().setAllowedTools(Set.of("*")); + when(accessKeyService.update(any(), any(), any(), any(), any(), anyBoolean())) + .thenReturn(reactor.core.publisher.Mono.just(updated)); + + WebTestClient.bindToRouterFunction(endpoint.endpoint()) + .build() + .put() + .uri("/keys/test-key") + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(""" + { + "displayName": "Test key", + "allowedTools": ["*"], + "allowedIpRanges": [], + "enabled": true + } + """) + .exchange() + .expectStatus() + .isOk() + .expectBody() + .jsonPath("$.allowedTools[0]") + .isEqualTo("*"); + + verify(accessKeyService).update( + "test-key", "Test key", Set.of("*"), Set.of(), null, true); + } + + @Test + void createsAKeyWithTheAllToolsWildcard() { + when(toolCatalog.availableNames()).thenReturn(reactor.core.publisher.Mono.just(Set.of())); + var key = new McpAccessKey(); + key.setMetadata(new Metadata()); + key.getMetadata().setName("test-key"); + key.getSpec().setDisplayName("Automation"); + key.getSpec().setKeyPrefix("hmcp_test"); + key.getSpec().setOwnerName("admin"); + key.getSpec().setAllowedTools(Set.of("*")); + when(accessKeyService.create("Automation", "admin", Set.of("*"), Set.of(), null)) + .thenReturn(reactor.core.publisher.Mono.just( + new McpAccessKeyService.CreatedKey(key, "secret"))); + + var authentication = new UsernamePasswordAuthenticationToken("admin", "n/a"); + WebTestClient.bindToRouterFunction(endpoint.endpoint()) + .webFilter((exchange, chain) -> chain.filter(exchange) + .contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))) + .build() + .post() + .uri("/keys") + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(""" + { + "displayName": "Automation", + "allowedTools": ["*"], + "allowedIpRanges": [] + } + """) + .exchange() + .expectStatus() + .isCreated() + .expectBody() + .jsonPath("$.key.allowedTools[0]") + .isEqualTo("*") + .jsonPath("$.token") + .isEqualTo("secret"); + + verify(accessKeyService).create("Automation", "admin", Set.of("*"), Set.of(), null); + } + @Test void listsRecentCallsWithFilters() { var call = new McpRecentCall( diff --git a/src/test/java/run/halo/mcpserver/McpAccessKeyServiceTest.java b/src/test/java/run/halo/mcpserver/McpAccessKeyServiceTest.java index 840c18f..bee9412 100644 --- a/src/test/java/run/halo/mcpserver/McpAccessKeyServiceTest.java +++ b/src/test/java/run/halo/mcpserver/McpAccessKeyServiceTest.java @@ -64,6 +64,19 @@ void createsAHashedKeyAndAuthenticatesIt() { assertThat(created.accessKey().getStatus().getLastUsedAt()).isNotNull(); } + @Test + void wildcardAllowsCurrentAndFutureTools() { + var created = service.create("Automation", "admin", Set.of("*"), Set.of(), null).block(); + when(client.fetch(McpAccessKey.class, created.accessKey().getMetadata().getName())) + .thenReturn(Mono.just(created.accessKey())); + + var authentication = service.authenticate(created.token(), null).block(); + + assertThat(authentication).isNotNull(); + assertThat(authentication.allows("halo_search_content")).isTrue(); + assertThat(authentication.allows("FuturePlugin__future_tool")).isTrue(); + } + @Test void rejectsDisabledAndExpiredKeys() { var created = service.create( diff --git a/ui/src/api/generated/models/create-mcp-access-key-request.ts b/ui/src/api/generated/models/create-mcp-access-key-request.ts index 24bc83c..3fcc16c 100644 --- a/ui/src/api/generated/models/create-mcp-access-key-request.ts +++ b/ui/src/api/generated/models/create-mcp-access-key-request.ts @@ -27,7 +27,7 @@ export interface CreateMcpAccessKeyRequest { */ 'allowedIpRanges': Array; /** - * + * Allowed MCP tool names. Use \'*\' to automatically allow all current and future tools. * @type {Array} * @memberof CreateMcpAccessKeyRequest */ diff --git a/ui/src/api/generated/models/mcp-access-key.ts b/ui/src/api/generated/models/mcp-access-key.ts index e5e4bf5..05569cb 100644 --- a/ui/src/api/generated/models/mcp-access-key.ts +++ b/ui/src/api/generated/models/mcp-access-key.ts @@ -27,7 +27,7 @@ export interface McpAccessKey { */ 'allowedIpRanges': Array; /** - * + * Allowed MCP tool names. Use \'*\' to automatically allow all current and future tools. * @type {Array} * @memberof McpAccessKey */ diff --git a/ui/src/api/generated/models/update-mcp-access-key-request.ts b/ui/src/api/generated/models/update-mcp-access-key-request.ts index 788c0b2..56b57a3 100644 --- a/ui/src/api/generated/models/update-mcp-access-key-request.ts +++ b/ui/src/api/generated/models/update-mcp-access-key-request.ts @@ -27,7 +27,7 @@ export interface UpdateMcpAccessKeyRequest { */ 'allowedIpRanges': Array; /** - * + * Allowed MCP tool names. Use \'*\' to automatically allow all current and future tools. * @type {Array} * @memberof UpdateMcpAccessKeyRequest */ diff --git a/ui/src/components/AccessKeyForm.vue b/ui/src/components/AccessKeyForm.vue index d4dade5..b56776c 100644 --- a/ui/src/components/AccessKeyForm.vue +++ b/ui/src/components/AccessKeyForm.vue @@ -1,9 +1,9 @@