diff --git a/backend/ent/group.go b/backend/ent/group.go index f91a4079..d2b6af9f 100644 --- a/backend/ent/group.go +++ b/backend/ent/group.go @@ -62,6 +62,8 @@ type Group struct { ModelRouting map[string][]int64 `json:"model_routing,omitempty"` // 是否启用模型路由配置 ModelRoutingEnabled bool `json:"model_routing_enabled,omitempty"` + // 是否注入 MCP XML 调用协议提示词(仅 antigravity 平台) + McpXMLInject bool `json:"mcp_xml_inject,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the GroupQuery when eager-loading is set. Edges GroupEdges `json:"edges"` @@ -170,7 +172,7 @@ func (*Group) scanValues(columns []string) ([]any, error) { switch columns[i] { case group.FieldModelRouting: values[i] = new([]byte) - case group.FieldIsExclusive, group.FieldClaudeCodeOnly, group.FieldModelRoutingEnabled: + case group.FieldIsExclusive, group.FieldClaudeCodeOnly, group.FieldModelRoutingEnabled, group.FieldMcpXMLInject: values[i] = new(sql.NullBool) case group.FieldRateMultiplier, group.FieldDailyLimitUsd, group.FieldWeeklyLimitUsd, group.FieldMonthlyLimitUsd, group.FieldImagePrice1k, group.FieldImagePrice2k, group.FieldImagePrice4k: values[i] = new(sql.NullFloat64) @@ -345,6 +347,12 @@ func (_m *Group) assignValues(columns []string, values []any) error { } else if value.Valid { _m.ModelRoutingEnabled = value.Bool } + case group.FieldMcpXMLInject: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field mcp_xml_inject", values[i]) + } else if value.Valid { + _m.McpXMLInject = value.Bool + } default: _m.selectValues.Set(columns[i], values[i]) } @@ -506,6 +514,9 @@ func (_m *Group) String() string { builder.WriteString(", ") builder.WriteString("model_routing_enabled=") builder.WriteString(fmt.Sprintf("%v", _m.ModelRoutingEnabled)) + builder.WriteString(", ") + builder.WriteString("mcp_xml_inject=") + builder.WriteString(fmt.Sprintf("%v", _m.McpXMLInject)) builder.WriteByte(')') return builder.String() } diff --git a/backend/ent/group/group.go b/backend/ent/group/group.go index b63827d3..aa9ff2ab 100644 --- a/backend/ent/group/group.go +++ b/backend/ent/group/group.go @@ -59,6 +59,8 @@ const ( FieldModelRouting = "model_routing" // FieldModelRoutingEnabled holds the string denoting the model_routing_enabled field in the database. FieldModelRoutingEnabled = "model_routing_enabled" + // FieldMcpXMLInject holds the string denoting the mcp_xml_inject field in the database. + FieldMcpXMLInject = "mcp_xml_inject" // EdgeAPIKeys holds the string denoting the api_keys edge name in mutations. EdgeAPIKeys = "api_keys" // EdgeRedeemCodes holds the string denoting the redeem_codes edge name in mutations. @@ -156,6 +158,7 @@ var Columns = []string{ FieldFallbackGroupIDOnInvalidRequest, FieldModelRouting, FieldModelRoutingEnabled, + FieldMcpXMLInject, } var ( @@ -215,6 +218,8 @@ var ( DefaultClaudeCodeOnly bool // DefaultModelRoutingEnabled holds the default value on creation for the "model_routing_enabled" field. DefaultModelRoutingEnabled bool + // DefaultMcpXMLInject holds the default value on creation for the "mcp_xml_inject" field. + DefaultMcpXMLInject bool ) // OrderOption defines the ordering options for the Group queries. @@ -330,6 +335,11 @@ func ByModelRoutingEnabled(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldModelRoutingEnabled, opts...).ToFunc() } +// ByMcpXMLInject orders the results by the mcp_xml_inject field. +func ByMcpXMLInject(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldMcpXMLInject, opts...).ToFunc() +} + // ByAPIKeysCount orders the results by api_keys count. func ByAPIKeysCount(opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { diff --git a/backend/ent/group/where.go b/backend/ent/group/where.go index 02cbb3d5..b6fa2c33 100644 --- a/backend/ent/group/where.go +++ b/backend/ent/group/where.go @@ -160,6 +160,11 @@ func ModelRoutingEnabled(v bool) predicate.Group { return predicate.Group(sql.FieldEQ(FieldModelRoutingEnabled, v)) } +// McpXMLInject applies equality check predicate on the "mcp_xml_inject" field. It's identical to McpXMLInjectEQ. +func McpXMLInject(v bool) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldMcpXMLInject, v)) +} + // CreatedAtEQ applies the EQ predicate on the "created_at" field. func CreatedAtEQ(v time.Time) predicate.Group { return predicate.Group(sql.FieldEQ(FieldCreatedAt, v)) @@ -1145,6 +1150,16 @@ func ModelRoutingEnabledNEQ(v bool) predicate.Group { return predicate.Group(sql.FieldNEQ(FieldModelRoutingEnabled, v)) } +// McpXMLInjectEQ applies the EQ predicate on the "mcp_xml_inject" field. +func McpXMLInjectEQ(v bool) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldMcpXMLInject, v)) +} + +// McpXMLInjectNEQ applies the NEQ predicate on the "mcp_xml_inject" field. +func McpXMLInjectNEQ(v bool) predicate.Group { + return predicate.Group(sql.FieldNEQ(FieldMcpXMLInject, v)) +} + // HasAPIKeys applies the HasEdge predicate on the "api_keys" edge. func HasAPIKeys() predicate.Group { return predicate.Group(func(s *sql.Selector) { diff --git a/backend/ent/group_create.go b/backend/ent/group_create.go index b08894da..b1ccc8e3 100644 --- a/backend/ent/group_create.go +++ b/backend/ent/group_create.go @@ -320,6 +320,20 @@ func (_c *GroupCreate) SetNillableModelRoutingEnabled(v *bool) *GroupCreate { return _c } +// SetMcpXMLInject sets the "mcp_xml_inject" field. +func (_c *GroupCreate) SetMcpXMLInject(v bool) *GroupCreate { + _c.mutation.SetMcpXMLInject(v) + return _c +} + +// SetNillableMcpXMLInject sets the "mcp_xml_inject" field if the given value is not nil. +func (_c *GroupCreate) SetNillableMcpXMLInject(v *bool) *GroupCreate { + if v != nil { + _c.SetMcpXMLInject(*v) + } + return _c +} + // AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs. func (_c *GroupCreate) AddAPIKeyIDs(ids ...int64) *GroupCreate { _c.mutation.AddAPIKeyIDs(ids...) @@ -493,6 +507,10 @@ func (_c *GroupCreate) defaults() error { v := group.DefaultModelRoutingEnabled _c.mutation.SetModelRoutingEnabled(v) } + if _, ok := _c.mutation.McpXMLInject(); !ok { + v := group.DefaultMcpXMLInject + _c.mutation.SetMcpXMLInject(v) + } return nil } @@ -551,6 +569,9 @@ func (_c *GroupCreate) check() error { if _, ok := _c.mutation.ModelRoutingEnabled(); !ok { return &ValidationError{Name: "model_routing_enabled", err: errors.New(`ent: missing required field "Group.model_routing_enabled"`)} } + if _, ok := _c.mutation.McpXMLInject(); !ok { + return &ValidationError{Name: "mcp_xml_inject", err: errors.New(`ent: missing required field "Group.mcp_xml_inject"`)} + } return nil } @@ -666,6 +687,10 @@ func (_c *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) { _spec.SetField(group.FieldModelRoutingEnabled, field.TypeBool, value) _node.ModelRoutingEnabled = value } + if value, ok := _c.mutation.McpXMLInject(); ok { + _spec.SetField(group.FieldMcpXMLInject, field.TypeBool, value) + _node.McpXMLInject = value + } if nodes := _c.mutation.APIKeysIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -1200,6 +1225,18 @@ func (u *GroupUpsert) UpdateModelRoutingEnabled() *GroupUpsert { return u } +// SetMcpXMLInject sets the "mcp_xml_inject" field. +func (u *GroupUpsert) SetMcpXMLInject(v bool) *GroupUpsert { + u.Set(group.FieldMcpXMLInject, v) + return u +} + +// UpdateMcpXMLInject sets the "mcp_xml_inject" field to the value that was provided on create. +func (u *GroupUpsert) UpdateMcpXMLInject() *GroupUpsert { + u.SetExcluded(group.FieldMcpXMLInject) + return u +} + // UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // @@ -1686,6 +1723,20 @@ func (u *GroupUpsertOne) UpdateModelRoutingEnabled() *GroupUpsertOne { }) } +// SetMcpXMLInject sets the "mcp_xml_inject" field. +func (u *GroupUpsertOne) SetMcpXMLInject(v bool) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetMcpXMLInject(v) + }) +} + +// UpdateMcpXMLInject sets the "mcp_xml_inject" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateMcpXMLInject() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateMcpXMLInject() + }) +} + // Exec executes the query. func (u *GroupUpsertOne) Exec(ctx context.Context) error { if len(u.create.conflict) == 0 { @@ -2338,6 +2389,20 @@ func (u *GroupUpsertBulk) UpdateModelRoutingEnabled() *GroupUpsertBulk { }) } +// SetMcpXMLInject sets the "mcp_xml_inject" field. +func (u *GroupUpsertBulk) SetMcpXMLInject(v bool) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetMcpXMLInject(v) + }) +} + +// UpdateMcpXMLInject sets the "mcp_xml_inject" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateMcpXMLInject() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateMcpXMLInject() + }) +} + // Exec executes the query. func (u *GroupUpsertBulk) Exec(ctx context.Context) error { if u.create.err != nil { diff --git a/backend/ent/group_update.go b/backend/ent/group_update.go index ce8f3748..332ae52a 100644 --- a/backend/ent/group_update.go +++ b/backend/ent/group_update.go @@ -448,6 +448,20 @@ func (_u *GroupUpdate) SetNillableModelRoutingEnabled(v *bool) *GroupUpdate { return _u } +// SetMcpXMLInject sets the "mcp_xml_inject" field. +func (_u *GroupUpdate) SetMcpXMLInject(v bool) *GroupUpdate { + _u.mutation.SetMcpXMLInject(v) + return _u +} + +// SetNillableMcpXMLInject sets the "mcp_xml_inject" field if the given value is not nil. +func (_u *GroupUpdate) SetNillableMcpXMLInject(v *bool) *GroupUpdate { + if v != nil { + _u.SetMcpXMLInject(*v) + } + return _u +} + // AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs. func (_u *GroupUpdate) AddAPIKeyIDs(ids ...int64) *GroupUpdate { _u.mutation.AddAPIKeyIDs(ids...) @@ -874,6 +888,9 @@ func (_u *GroupUpdate) sqlSave(ctx context.Context) (_node int, err error) { if value, ok := _u.mutation.ModelRoutingEnabled(); ok { _spec.SetField(group.FieldModelRoutingEnabled, field.TypeBool, value) } + if value, ok := _u.mutation.McpXMLInject(); ok { + _spec.SetField(group.FieldMcpXMLInject, field.TypeBool, value) + } if _u.mutation.APIKeysCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -1602,6 +1619,20 @@ func (_u *GroupUpdateOne) SetNillableModelRoutingEnabled(v *bool) *GroupUpdateOn return _u } +// SetMcpXMLInject sets the "mcp_xml_inject" field. +func (_u *GroupUpdateOne) SetMcpXMLInject(v bool) *GroupUpdateOne { + _u.mutation.SetMcpXMLInject(v) + return _u +} + +// SetNillableMcpXMLInject sets the "mcp_xml_inject" field if the given value is not nil. +func (_u *GroupUpdateOne) SetNillableMcpXMLInject(v *bool) *GroupUpdateOne { + if v != nil { + _u.SetMcpXMLInject(*v) + } + return _u +} + // AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs. func (_u *GroupUpdateOne) AddAPIKeyIDs(ids ...int64) *GroupUpdateOne { _u.mutation.AddAPIKeyIDs(ids...) @@ -2058,6 +2089,9 @@ func (_u *GroupUpdateOne) sqlSave(ctx context.Context) (_node *Group, err error) if value, ok := _u.mutation.ModelRoutingEnabled(); ok { _spec.SetField(group.FieldModelRoutingEnabled, field.TypeBool, value) } + if value, ok := _u.mutation.McpXMLInject(); ok { + _spec.SetField(group.FieldMcpXMLInject, field.TypeBool, value) + } if _u.mutation.APIKeysCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/backend/ent/migrate/schema.go b/backend/ent/migrate/schema.go index 5624c05b..3b83061e 100644 --- a/backend/ent/migrate/schema.go +++ b/backend/ent/migrate/schema.go @@ -229,6 +229,7 @@ var ( {Name: "fallback_group_id_on_invalid_request", Type: field.TypeInt64, Nullable: true}, {Name: "model_routing", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}}, {Name: "model_routing_enabled", Type: field.TypeBool, Default: false}, + {Name: "mcp_xml_inject", Type: field.TypeBool, Default: true}, } // GroupsTable holds the schema information for the "groups" table. GroupsTable = &schema.Table{ diff --git a/backend/ent/mutation.go b/backend/ent/mutation.go index 69801b9f..98195985 100644 --- a/backend/ent/mutation.go +++ b/backend/ent/mutation.go @@ -3868,6 +3868,7 @@ type GroupMutation struct { addfallback_group_id_on_invalid_request *int64 model_routing *map[string][]int64 model_routing_enabled *bool + mcp_xml_inject *bool clearedFields map[string]struct{} api_keys map[int64]struct{} removedapi_keys map[int64]struct{} @@ -5133,6 +5134,42 @@ func (m *GroupMutation) ResetModelRoutingEnabled() { m.model_routing_enabled = nil } +// SetMcpXMLInject sets the "mcp_xml_inject" field. +func (m *GroupMutation) SetMcpXMLInject(b bool) { + m.mcp_xml_inject = &b +} + +// McpXMLInject returns the value of the "mcp_xml_inject" field in the mutation. +func (m *GroupMutation) McpXMLInject() (r bool, exists bool) { + v := m.mcp_xml_inject + if v == nil { + return + } + return *v, true +} + +// OldMcpXMLInject returns the old "mcp_xml_inject" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldMcpXMLInject(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMcpXMLInject is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMcpXMLInject requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMcpXMLInject: %w", err) + } + return oldValue.McpXMLInject, nil +} + +// ResetMcpXMLInject resets all changes to the "mcp_xml_inject" field. +func (m *GroupMutation) ResetMcpXMLInject() { + m.mcp_xml_inject = nil +} + // AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by ids. func (m *GroupMutation) AddAPIKeyIDs(ids ...int64) { if m.api_keys == nil { @@ -5491,7 +5528,7 @@ func (m *GroupMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *GroupMutation) Fields() []string { - fields := make([]string, 0, 22) + fields := make([]string, 0, 23) if m.created_at != nil { fields = append(fields, group.FieldCreatedAt) } @@ -5558,6 +5595,9 @@ func (m *GroupMutation) Fields() []string { if m.model_routing_enabled != nil { fields = append(fields, group.FieldModelRoutingEnabled) } + if m.mcp_xml_inject != nil { + fields = append(fields, group.FieldMcpXMLInject) + } return fields } @@ -5610,6 +5650,8 @@ func (m *GroupMutation) Field(name string) (ent.Value, bool) { return m.ModelRouting() case group.FieldModelRoutingEnabled: return m.ModelRoutingEnabled() + case group.FieldMcpXMLInject: + return m.McpXMLInject() } return nil, false } @@ -5663,6 +5705,8 @@ func (m *GroupMutation) OldField(ctx context.Context, name string) (ent.Value, e return m.OldModelRouting(ctx) case group.FieldModelRoutingEnabled: return m.OldModelRoutingEnabled(ctx) + case group.FieldMcpXMLInject: + return m.OldMcpXMLInject(ctx) } return nil, fmt.Errorf("unknown Group field %s", name) } @@ -5826,6 +5870,13 @@ func (m *GroupMutation) SetField(name string, value ent.Value) error { } m.SetModelRoutingEnabled(v) return nil + case group.FieldMcpXMLInject: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMcpXMLInject(v) + return nil } return fmt.Errorf("unknown Group field %s", name) } @@ -6133,6 +6184,9 @@ func (m *GroupMutation) ResetField(name string) error { case group.FieldModelRoutingEnabled: m.ResetModelRoutingEnabled() return nil + case group.FieldMcpXMLInject: + m.ResetMcpXMLInject() + return nil } return fmt.Errorf("unknown Group field %s", name) } diff --git a/backend/ent/runtime/runtime.go b/backend/ent/runtime/runtime.go index 3ddb206d..e7f33598 100644 --- a/backend/ent/runtime/runtime.go +++ b/backend/ent/runtime/runtime.go @@ -284,6 +284,10 @@ func init() { groupDescModelRoutingEnabled := groupFields[18].Descriptor() // group.DefaultModelRoutingEnabled holds the default value on creation for the model_routing_enabled field. group.DefaultModelRoutingEnabled = groupDescModelRoutingEnabled.Default.(bool) + // groupDescMcpXMLInject is the schema descriptor for mcp_xml_inject field. + groupDescMcpXMLInject := groupFields[19].Descriptor() + // group.DefaultMcpXMLInject holds the default value on creation for the mcp_xml_inject field. + group.DefaultMcpXMLInject = groupDescMcpXMLInject.Default.(bool) promocodeFields := schema.PromoCode{}.Fields() _ = promocodeFields // promocodeDescCode is the schema descriptor for code field. diff --git a/backend/ent/schema/group.go b/backend/ent/schema/group.go index 51cae1a6..58583752 100644 --- a/backend/ent/schema/group.go +++ b/backend/ent/schema/group.go @@ -110,6 +110,11 @@ func (Group) Fields() []ent.Field { field.Bool("model_routing_enabled"). Default(false). Comment("是否启用模型路由配置"), + + // MCP XML 协议注入开关 (added by migration 042) + field.Bool("mcp_xml_inject"). + Default(true). + Comment("是否注入 MCP XML 调用协议提示词(仅 antigravity 平台)"), } } diff --git a/backend/internal/handler/admin/group_handler.go b/backend/internal/handler/admin/group_handler.go index 8229a780..1df5af8c 100644 --- a/backend/internal/handler/admin/group_handler.go +++ b/backend/internal/handler/admin/group_handler.go @@ -44,6 +44,7 @@ type CreateGroupRequest struct { // 模型路由配置(仅 anthropic 平台使用) ModelRouting map[string][]int64 `json:"model_routing"` ModelRoutingEnabled bool `json:"model_routing_enabled"` + MCPXMLInject *bool `json:"mcp_xml_inject"` } // UpdateGroupRequest represents update group request @@ -68,6 +69,7 @@ type UpdateGroupRequest struct { // 模型路由配置(仅 anthropic 平台使用) ModelRouting map[string][]int64 `json:"model_routing"` ModelRoutingEnabled *bool `json:"model_routing_enabled"` + MCPXMLInject *bool `json:"mcp_xml_inject"` } // List handles listing all groups with pagination @@ -174,6 +176,7 @@ func (h *GroupHandler) Create(c *gin.Context) { FallbackGroupIDOnInvalidRequest: req.FallbackGroupIDOnInvalidRequest, ModelRouting: req.ModelRouting, ModelRoutingEnabled: req.ModelRoutingEnabled, + MCPXMLInject: req.MCPXMLInject, }) if err != nil { response.ErrorFrom(c, err) @@ -217,6 +220,7 @@ func (h *GroupHandler) Update(c *gin.Context) { FallbackGroupIDOnInvalidRequest: req.FallbackGroupIDOnInvalidRequest, ModelRouting: req.ModelRouting, ModelRoutingEnabled: req.ModelRoutingEnabled, + MCPXMLInject: req.MCPXMLInject, }) if err != nil { response.ErrorFrom(c, err) diff --git a/backend/internal/handler/dto/mappers.go b/backend/internal/handler/dto/mappers.go index 18bbec31..e8420336 100644 --- a/backend/internal/handler/dto/mappers.go +++ b/backend/internal/handler/dto/mappers.go @@ -92,6 +92,7 @@ func GroupFromServiceShallow(g *service.Group) *Group { FallbackGroupIDOnInvalidRequest: g.FallbackGroupIDOnInvalidRequest, ModelRouting: g.ModelRouting, ModelRoutingEnabled: g.ModelRoutingEnabled, + MCPXMLInject: g.MCPXMLInject, CreatedAt: g.CreatedAt, UpdatedAt: g.UpdatedAt, AccountCount: g.AccountCount, diff --git a/backend/internal/handler/dto/types.go b/backend/internal/handler/dto/types.go index 97bd2eca..abb9494a 100644 --- a/backend/internal/handler/dto/types.go +++ b/backend/internal/handler/dto/types.go @@ -69,6 +69,9 @@ type Group struct { ModelRouting map[string][]int64 `json:"model_routing"` ModelRoutingEnabled bool `json:"model_routing_enabled"` + // MCP XML 协议注入(仅 antigravity 平台使用) + MCPXMLInject bool `json:"mcp_xml_inject"` + CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` diff --git a/backend/internal/pkg/antigravity/request_transformer.go b/backend/internal/pkg/antigravity/request_transformer.go index 80063cb8..720e6f6a 100644 --- a/backend/internal/pkg/antigravity/request_transformer.go +++ b/backend/internal/pkg/antigravity/request_transformer.go @@ -44,11 +44,13 @@ type TransformOptions struct { // IdentityPatch 可选:自定义注入到 systemInstruction 开头的身份防护提示词; // 为空时使用默认模板(包含 [IDENTITY_PATCH] 及 SYSTEM_PROMPT_BEGIN 标记)。 IdentityPatch string + EnableMCPXML bool } func DefaultTransformOptions() TransformOptions { return TransformOptions{ EnableIdentityPatch: true, + EnableMCPXML: true, } } @@ -257,8 +259,8 @@ func buildSystemInstruction(system json.RawMessage, modelName string, opts Trans // 添加用户的 system prompt parts = append(parts, userSystemParts...) - // 检测是否有 MCP 工具,如有则注入 XML 调用协议 - if hasMCPTools(tools) { + // 检测是否有 MCP 工具,如有且启用了 MCP XML 注入则注入 XML 调用协议 + if opts.EnableMCPXML && hasMCPTools(tools) { parts = append(parts, GeminiPart{Text: mcpXMLProtocol}) } @@ -491,7 +493,7 @@ func parseToolResultContent(content json.RawMessage, isError bool) string { // buildGenerationConfig 构建 generationConfig const ( - defaultMaxOutputTokens = 64000 + defaultMaxOutputTokens = 64000 maxOutputTokensUpperBound = 65000 maxOutputTokensClaude = 64000 ) diff --git a/backend/internal/repository/api_key_repo.go b/backend/internal/repository/api_key_repo.go index 9938a36d..e9af365c 100644 --- a/backend/internal/repository/api_key_repo.go +++ b/backend/internal/repository/api_key_repo.go @@ -139,6 +139,7 @@ func (r *apiKeyRepository) GetByKeyForAuth(ctx context.Context, key string) (*se group.FieldFallbackGroupIDOnInvalidRequest, group.FieldModelRoutingEnabled, group.FieldModelRouting, + group.FieldMcpXMLInject, ) }). Only(ctx) @@ -428,6 +429,7 @@ func groupEntityToService(g *dbent.Group) *service.Group { FallbackGroupIDOnInvalidRequest: g.FallbackGroupIDOnInvalidRequest, ModelRouting: g.ModelRouting, ModelRoutingEnabled: g.ModelRoutingEnabled, + MCPXMLInject: g.McpXMLInject, CreatedAt: g.CreatedAt, UpdatedAt: g.UpdatedAt, } diff --git a/backend/internal/repository/group_repo.go b/backend/internal/repository/group_repo.go index f207f479..116e45a3 100644 --- a/backend/internal/repository/group_repo.go +++ b/backend/internal/repository/group_repo.go @@ -51,7 +51,8 @@ func (r *groupRepository) Create(ctx context.Context, groupIn *service.Group) er SetClaudeCodeOnly(groupIn.ClaudeCodeOnly). SetNillableFallbackGroupID(groupIn.FallbackGroupID). SetNillableFallbackGroupIDOnInvalidRequest(groupIn.FallbackGroupIDOnInvalidRequest). - SetModelRoutingEnabled(groupIn.ModelRoutingEnabled) + SetModelRoutingEnabled(groupIn.ModelRoutingEnabled). + SetMcpXMLInject(groupIn.MCPXMLInject) // 设置模型路由配置 if groupIn.ModelRouting != nil { @@ -109,7 +110,8 @@ func (r *groupRepository) Update(ctx context.Context, groupIn *service.Group) er SetNillableImagePrice4k(groupIn.ImagePrice4K). SetDefaultValidityDays(groupIn.DefaultValidityDays). SetClaudeCodeOnly(groupIn.ClaudeCodeOnly). - SetModelRoutingEnabled(groupIn.ModelRoutingEnabled) + SetModelRoutingEnabled(groupIn.ModelRoutingEnabled). + SetMcpXMLInject(groupIn.MCPXMLInject) // 处理 FallbackGroupID:nil 时清除,否则设置 if groupIn.FallbackGroupID != nil { diff --git a/backend/internal/service/admin_service.go b/backend/internal/service/admin_service.go index 12f01810..392c7aa2 100644 --- a/backend/internal/service/admin_service.go +++ b/backend/internal/service/admin_service.go @@ -113,6 +113,7 @@ type CreateGroupInput struct { // 模型路由配置(仅 anthropic 平台使用) ModelRouting map[string][]int64 ModelRoutingEnabled bool // 是否启用模型路由 + MCPXMLInject *bool } type UpdateGroupInput struct { @@ -137,6 +138,7 @@ type UpdateGroupInput struct { // 模型路由配置(仅 anthropic 平台使用) ModelRouting map[string][]int64 ModelRoutingEnabled *bool // 是否启用模型路由 + MCPXMLInject *bool } type CreateAccountInput struct { @@ -587,6 +589,12 @@ func (s *adminServiceImpl) CreateGroup(ctx context.Context, input *CreateGroupIn } } + // MCPXMLInject:默认为 true,仅当显式传入 false 时关闭 + mcpXMLInject := true + if input.MCPXMLInject != nil { + mcpXMLInject = *input.MCPXMLInject + } + group := &Group{ Name: input.Name, Description: input.Description, @@ -605,6 +613,7 @@ func (s *adminServiceImpl) CreateGroup(ctx context.Context, input *CreateGroupIn FallbackGroupID: input.FallbackGroupID, FallbackGroupIDOnInvalidRequest: fallbackOnInvalidRequest, ModelRouting: input.ModelRouting, + MCPXMLInject: mcpXMLInject, } if err := s.groupRepo.Create(ctx, group); err != nil { return nil, err @@ -785,6 +794,9 @@ func (s *adminServiceImpl) UpdateGroup(ctx context.Context, id int64, input *Upd if input.ModelRoutingEnabled != nil { group.ModelRoutingEnabled = *input.ModelRoutingEnabled } + if input.MCPXMLInject != nil { + group.MCPXMLInject = *input.MCPXMLInject + } if err := s.groupRepo.Update(ctx, group); err != nil { return nil, err diff --git a/backend/internal/service/antigravity_gateway_service.go b/backend/internal/service/antigravity_gateway_service.go index d3c15418..dbdfd374 100644 --- a/backend/internal/service/antigravity_gateway_service.go +++ b/backend/internal/service/antigravity_gateway_service.go @@ -19,6 +19,7 @@ import ( "time" "github.com/Wei-Shaw/sub2api/internal/pkg/antigravity" + "github.com/Wei-Shaw/sub2api/internal/pkg/ctxkey" "github.com/gin-gonic/gin" "github.com/google/uuid" ) @@ -552,6 +553,10 @@ func (s *AntigravityGatewayService) getClaudeTransformOptions(ctx context.Contex } opts.EnableIdentityPatch = s.settingService.IsIdentityPatchEnabled(ctx) opts.IdentityPatch = s.settingService.GetIdentityPatchPrompt(ctx) + + if group, ok := ctx.Value(ctxkey.Group).(*Group); ok && group != nil { + opts.EnableMCPXML = group.MCPXMLInject + } return opts } diff --git a/backend/internal/service/api_key_auth_cache.go b/backend/internal/service/api_key_auth_cache.go index 4b51fbbb..5cb2fbfb 100644 --- a/backend/internal/service/api_key_auth_cache.go +++ b/backend/internal/service/api_key_auth_cache.go @@ -43,6 +43,7 @@ type APIKeyAuthGroupSnapshot struct { // Only anthropic groups use these fields; others may leave them empty. ModelRouting map[string][]int64 `json:"model_routing,omitempty"` ModelRoutingEnabled bool `json:"model_routing_enabled"` + MCPXMLInject bool `json:"mcp_xml_inject"` } // APIKeyAuthCacheEntry 缓存条目,支持负缓存 diff --git a/backend/internal/service/api_key_auth_cache_impl.go b/backend/internal/service/api_key_auth_cache_impl.go index 8b74e7aa..b9f0ef36 100644 --- a/backend/internal/service/api_key_auth_cache_impl.go +++ b/backend/internal/service/api_key_auth_cache_impl.go @@ -224,6 +224,7 @@ func (s *APIKeyService) snapshotFromAPIKey(apiKey *APIKey) *APIKeyAuthSnapshot { FallbackGroupIDOnInvalidRequest: apiKey.Group.FallbackGroupIDOnInvalidRequest, ModelRouting: apiKey.Group.ModelRouting, ModelRoutingEnabled: apiKey.Group.ModelRoutingEnabled, + MCPXMLInject: apiKey.Group.MCPXMLInject, } } return snapshot @@ -269,6 +270,7 @@ func (s *APIKeyService) snapshotToAPIKey(key string, snapshot *APIKeyAuthSnapsho FallbackGroupIDOnInvalidRequest: snapshot.Group.FallbackGroupIDOnInvalidRequest, ModelRouting: snapshot.Group.ModelRouting, ModelRoutingEnabled: snapshot.Group.ModelRoutingEnabled, + MCPXMLInject: snapshot.Group.MCPXMLInject, } } return apiKey diff --git a/backend/internal/service/group.go b/backend/internal/service/group.go index 9140b6d9..7f1825c6 100644 --- a/backend/internal/service/group.go +++ b/backend/internal/service/group.go @@ -38,6 +38,9 @@ type Group struct { ModelRouting map[string][]int64 ModelRoutingEnabled bool + // MCP XML 协议注入开关(仅 antigravity 平台使用) + MCPXMLInject bool + CreatedAt time.Time UpdatedAt time.Time diff --git a/backend/migrations/044_add_group_mcp_xml_inject.sql b/backend/migrations/044_add_group_mcp_xml_inject.sql new file mode 100644 index 00000000..7db71dd8 --- /dev/null +++ b/backend/migrations/044_add_group_mcp_xml_inject.sql @@ -0,0 +1,2 @@ +-- Add mcp_xml_inject field to groups table (for antigravity platform) +ALTER TABLE groups ADD COLUMN mcp_xml_inject BOOLEAN NOT NULL DEFAULT true; diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 0e4effc9..84d1e641 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -943,6 +943,12 @@ export default { noRulesHint: 'Add routing rules to route specific model requests to designated accounts', searchAccountPlaceholder: 'Search accounts...', accountsHint: 'Select accounts to prioritize for this model pattern' + }, + mcpXml: { + title: 'MCP XML Protocol Injection', + tooltip: 'When enabled, if the request contains MCP tools, an XML format call protocol prompt will be injected into the system prompt. Disable this to avoid interference with certain clients.', + enabled: 'Enabled', + disabled: 'Disabled' } }, diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index ae6b2abf..468ee5ad 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -1019,6 +1019,12 @@ export default { noRulesHint: '添加路由规则以将特定模型请求优先路由到指定账号', searchAccountPlaceholder: '搜索账号...', accountsHint: '选择此模型模式优先使用的账号' + }, + mcpXml: { + title: 'MCP XML 协议注入', + tooltip: '启用后,当请求包含 MCP 工具时,会在 system prompt 中注入 XML 格式调用协议提示词。关闭此选项可避免对某些客户端造成干扰。', + enabled: '已启用', + disabled: '已禁用' } }, diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 17377c98..1e23a85f 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -273,6 +273,8 @@ export interface Group { // 模型路由配置(仅 anthropic 平台使用) model_routing: Record | null model_routing_enabled: boolean + // MCP XML 协议注入(仅 antigravity 平台使用) + mcp_xml_inject: boolean account_count?: number created_at: string updated_at: string diff --git a/frontend/src/views/admin/GroupsView.vue b/frontend/src/views/admin/GroupsView.vue index f3a407d7..ffeb960e 100644 --- a/frontend/src/views/admin/GroupsView.vue +++ b/frontend/src/views/admin/GroupsView.vue @@ -404,6 +404,51 @@ + +
+
+ +
+ +
+
+

+ {{ t('admin.groups.mcpXml.tooltip') }} +

+
+
+
+
+
+
+ + + {{ createForm.mcp_xml_inject ? t('admin.groups.mcpXml.enabled') : t('admin.groups.mcpXml.disabled') }} + +
+
+
@@ -862,6 +907,51 @@
+ +
+
+ +
+ +
+
+

+ {{ t('admin.groups.mcpXml.tooltip') }} +

+
+
+
+
+
+
+ + + {{ editForm.mcp_xml_inject ? t('admin.groups.mcpXml.enabled') : t('admin.groups.mcpXml.disabled') }} + +
+
+
@@ -1311,7 +1401,8 @@ const createForm = reactive({ fallback_group_id: null as number | null, fallback_group_id_on_invalid_request: null as number | null, // 模型路由开关 - model_routing_enabled: false + model_routing_enabled: false, + mcp_xml_inject: true }) // 简单账号类型(用于模型路由选择) @@ -1483,7 +1574,8 @@ const editForm = reactive({ fallback_group_id: null as number | null, fallback_group_id_on_invalid_request: null as number | null, // 模型路由开关 - model_routing_enabled: false + model_routing_enabled: false, + mcp_xml_inject: true }) // 根据分组类型返回不同的删除确认消息 @@ -1566,6 +1658,7 @@ const closeCreateModal = () => { createForm.claude_code_only = false createForm.fallback_group_id = null createForm.fallback_group_id_on_invalid_request = null + createForm.mcp_xml_inject = true createModelRoutingRules.value = [] } @@ -1617,6 +1710,7 @@ const handleEdit = async (group: Group) => { editForm.fallback_group_id = group.fallback_group_id editForm.fallback_group_id_on_invalid_request = group.fallback_group_id_on_invalid_request editForm.model_routing_enabled = group.model_routing_enabled || false + editForm.mcp_xml_inject = group.mcp_xml_inject ?? true // 加载模型路由规则(异步加载账号名称) editModelRoutingRules.value = await convertApiFormatToRoutingRules(group.model_routing) showEditModal.value = true