update case interface

This commit is contained in:
rookit
2026-05-18 15:59:35 +08:00
parent 103f2de9b8
commit f94b92e7d7
6 changed files with 81 additions and 188 deletions
+10 -13
View File
@@ -21,7 +21,6 @@ Case is the core investigation object in ASP. One case can have one or more aler
- The user gives a case ID and wants to review, triage, or quickly summarize it.
- The user wants to find cases by status, severity, confidence, verdict, correlation UID, title, or tags.
- The user wants case discussion context.
- The user wants to update case workflow fields or AI analysis fields.
- The user wants to attach enrichment or structured analysis to a case.
- The user wants to attach an external ticket record to a case.
@@ -38,10 +37,9 @@ Case is the core investigation object in ASP. One case can have one or more aler
## Decision Flow
1. If the user provides a specific case ID or says "open", "show", "review", or "summarize" a case, call `list_cases(case_id=<id>, limit=1)`.
2. If the user wants discussion history or analyst context, call `get_case_discussions` after retrieving the case.
3. If the user wants to browse or compare cases, use `list_cases`.
4. If the user wants to change status, verdict, severity, or AI fields, use `update_case`.
1. If the user provides a specific case ID or says "open", "show", "review", or "summarize" a case, call `list_cases(case_id=<id>, limit=1)`. Discussions are included by default.
2. If the user wants to browse or compare cases, use `list_cases`. Pass `include_discussions=False` if discussions are not needed.
3. If the user wants to change status, verdict, severity, or AI fields, use `update_case`.
5. If the user wants to update a case but did not provide a case ID, ask for the case ID.
6. If the user gives multiple filters, apply only the ones ASP supports directly and state any unsupported filters explicitly.
7. If the user wants to attach enrichment or structured analysis to the case, use the `asp-enrichment-en` skill.
@@ -51,12 +49,11 @@ Case is the core investigation object in ASP. One case can have one or more aler
### Review One Case
1. If the user wants to review, analyze, or inspect case details, call `list_cases(case_id=<id>, limit=1, lazy_load=false)` to fetch the full related data, including alerts, enrichments, and tickets.
2. If the user only needs the basic case information, call `list_cases(case_id=<id>, limit=1)`.
1. If the user wants to review, analyze, or inspect case details, call `list_cases(case_id=<id>, limit=1, lazy_load=false)` to fetch the full related data, including alerts, enrichments, and tickets. Discussions are included by default.
2. If the user only needs the basic case information, call `list_cases(case_id=<id>, limit=1, include_discussions=false)`.
3. If the result is empty, state that the case was not found.
4. If the user wants analyst context, call `get_case_discussions(case_id)`.
5. Present only the parts most relevant to the user's request.
6. Only emphasize missing or suspicious fields when they matter to the user's goal.
4. Present only the parts most relevant to the user's request.
5. Only emphasize missing or suspicious fields when they matter to the user's goal.
Preferred response structure:
@@ -105,9 +102,9 @@ Common update targets:
- `verdict`
- `severity_ai`
- `confidence_ai`
- `attack_stage_ai`
- `comment_ai`
- `summary_ai`
- `verdict_ai`
- `comment`
- `summary`
Preferred response structure:
+10 -13
View File
@@ -22,7 +22,6 @@ Case。
- 用户给出一个 case ID,希望查看、分诊或快速总结。
- 用户希望按状态、严重级别、置信度、verdict、correlation UID、标题或标签查找 case。
- 用户想查看 case 的讨论上下文。
- 用户想更新 case 工作流字段或 AI 分析字段。
- 用户想把 enrichment 或结构化分析附加到 case。
- 用户想把外部 ticket 记录附加到 case。
@@ -38,10 +37,9 @@ Case。
## 决策流程
1. 如果用户提供了具体 case ID,或说要open”show”review”summarize”某个 case,调用 `list_cases(case_id=<id>, limit=1)`
2. 如果用户想看讨论历史或分析上下文,在取回 case 后调`get_case_discussions`
3. 如果用户要查找、浏览或对比 case,使用 `list_cases`
4. 如果用户要修改 status、verdict、severity 或 AI 字段,使用 `update_case`
1. 如果用户提供了具体 case ID,或说要open”show”review”summarize”某个 case,调用 `list_cases(case_id=<id>, limit=1)`默认已包含讨论记录。
2. 如果用户要查找、浏览或对比 case,使`list_cases`。不需要讨论时传 `include_discussions=False`
3. 如果用户要修改 status、verdict、severity 或 AI 字段,使用 `update_case`
5. 如果用户要更新 case 但没提供 case ID,询问 case ID。
6. 如果用户给出了多个过滤条件,只应用 ASP 直接支持的部分,并明确说明不支持的过滤条件。
7. 如果用户要把 enrichment 或结构化分析附加到 case,使用 `asp-enrichment-zh` skill。
@@ -52,12 +50,11 @@ Case。
### 审查单个 Case
1. 如果用户要求审查、分析或查看 case 详情,调用 `list_cases(case_id=<id>, limit=1, lazy_load=false)`
获取完整关联数据(alerts、enrichments、tickets)。
2. 如果只需要快速查看 case 基本信息,调用 `list_cases(case_id=<id>, limit=1)` 即可。
获取完整关联数据(alerts、enrichments、tickets)。默认已包含讨论记录。
2. 如果只需要快速查看 case 基本信息,调用 `list_cases(case_id=<id>, limit=1, include_discussions=false)` 即可。
3. 如果结果为空,直接说明找不到该 case。
4. 如果用户想看分析上下文,调用 `get_case_discussions(case_id)`
5.展示与用户请求最相关的部分
6. 只有在确实影响用户目标时,才强调缺失字段或可疑字段。
4. 只展示与用户请求最相关的部分
5.有在确实影响用户目标时,才强调缺失字段或可疑字段
首选回复结构:
@@ -106,9 +103,9 @@ Case。
- `verdict`
- `severity_ai`
- `confidence_ai`
- `attack_stage_ai`
- `comment_ai`
- `summary_ai`
- `verdict_ai`
- `comment`
- `summary`
首选回复结构:
@@ -1,11 +1,11 @@
---
name: asp-ticket-en
description: 'Sync external tickets into ASP, attach tickets to cases, list synced tickets, or update existing ticket records.'
argument-hint: 'list tickets [filters] | create ticket <uid> | attach ticket to case <case_id> | update ticket <ticket_id> <fields>'
description: 'Sync external tickets into ASP, create tickets linked to cases, list synced tickets, or update existing ticket records.'
argument-hint: 'list tickets [filters] | create ticket <case_id> <uid> | update ticket <ticket_id> <fields>'
compatibility: connect to asp mcp server
metadata:
author: Funnywolf
version: 0.1.0
version: 0.2.0
mcp-server: asp
category: cyber security
tags: [ ticket, case, sync, workflow ]
@@ -18,25 +18,22 @@ Use this skill when the user needs to sync external tickets on ASP.
## When to Use
- The user wants to create a synced external ticket record.
- The user wants to attach a ticket to a case.
- The user wants to create a synced external ticket record linked to a case.
- The user wants to list synced tickets by status, type, or external UID.
- The user wants to update synced ticket fields.
## Operating Rules
- Treat tickets as synced external workflow records, not as the platform's main investigation object.
- Use `create_ticket` to create the synced ticket record.
- Use `attach_ticket_to_case` to link an existing ticket record to a case after you already have the ticket row_id.
- Use `create_ticket` to create the synced ticket record and attach it to a case in one step.
- Use `list_tickets` for browsing and lookup.
- Use `update_ticket` only for fields the user explicitly wants changed.
## Decision Flow
1. If the user wants to create a synced ticket record, call `create_ticket`.
2. If the user wants to attach a ticket to a case, create the ticket when needed or retrieve the existing ticket row_id first, then call `attach_ticket_to_case`.
3. If the user wants to browse or compare synced tickets, call `list_tickets`.
4. If the user wants to update synced ticket fields, call `update_ticket`.
1. If the user wants to create a synced ticket record linked to a case, call `create_ticket(case_id=..., uid=...)`.
2. If the user wants to browse or compare synced tickets, call `list_tickets`.
3. If the user wants to update synced ticket fields, call `update_ticket`.
## SOP
@@ -45,7 +42,7 @@ Use this skill when the user needs to sync external tickets on ASP.
1. Extract the narrowest useful filters from the request.
2. Call `list_tickets`.
3. Parse the returned JSON strings.
4. Present a compact workflow-oriented view, and surface the ticket row_id when the user will likely attach or reuse the ticket next.
4. Present a compact workflow-oriented view, and surface the ticket row_id when the user will likely reuse the ticket next.
Preferred response structure:
@@ -56,17 +53,10 @@ Then add one short explanation line when needed.
### Create Ticket
1. Collect the external ticket details the user wants to sync.
2. Call `create_ticket`.
3. Confirm the created ticket row_id.
4. If the ticket should be linked to a case, suggest attaching it next.
### Attach Ticket To Case
1. Require `case_id`.
2. If the user does not already have a ticket row_id, either call `create_ticket` for a new ticket or retrieve the existing ticket first.
3. Call `attach_ticket_to_case(case_id=<case_id>, ticket_row_id=<ticket_row_id>)`.
4. Confirm that the ticket is attached.
2. Collect the external ticket details the user wants to sync.
3. Call `create_ticket(case_id=..., uid=..., ...)`.
4. Confirm the created ticket row_id and that it is attached to the case.
### Update Ticket
@@ -80,13 +70,11 @@ Preferred response structure:
- `Updated ticket`: ticket ID or returned row_id
- `Changed fields`: only the fields sent in the request
- `Next useful step`: optional, usually to attach it to a case or review the refreshed ticket
## Clarification Rules
- Ask for `case_id` only when the user wants case attachment and did not provide it.
- Ask for `case_id` only when creating a ticket and it was not provided.
- Ask for `ticket_id` only when the user wants to update a specific synced ticket and did not provide it.
- If the user wants to create a ticket and attach it in one request, do both steps without forcing them to separate the workflow.
## Output Rules
@@ -1,11 +1,11 @@
---
name: asp-ticket-zh
description: '把外部 ticket 同步到 ASP、把 ticket 关联到 case、列出已同步 ticket,或更新已有 ticket 记录。'
argument-hint: 'list tickets [filters] | create ticket <uid> | attach ticket to case <case_id> | update ticket <ticket_id> <fields>'
description: '把外部 ticket 同步到 ASP、创建并关联到 case、列出已同步 ticket,或更新已有 ticket 记录。'
argument-hint: 'list tickets [filters] | create ticket <case_id> <uid> | update ticket <ticket_id> <fields>'
compatibility: connect to asp mcp server
metadata:
author: Funnywolf
version: 0.1.0
version: 0.2.0
mcp-server: asp
category: cyber security
tags: [ ticket, case, sync, workflow ]
@@ -18,25 +18,22 @@ metadata:
## 适用场景
- 用户想创建一条已同步的外部 ticket 记录。
- 用户想把 ticket 关联到 case。
- 用户想创建一条已同步的外部 ticket 记录并关联到 case
- 用户想按状态、类型或外部 UID 列出已同步 ticket。
- 用户想更新已同步 ticket 的字段。
## 运行规则
- 把 ticket 视为已同步的外部工作流记录,而不是平台的主要调查对象。
- 使用 `create_ticket` 创建已同步 ticket 记录。
- 使用 `attach_ticket_to_case` 把已有 ticket 记录关联到 case,前提是已经拿到 ticket row_id。
- 使用 `create_ticket` 创建已同步 ticket 记录并自动关联到 case
- 使用 `list_tickets` 浏览和查询。
- 使用 `update_ticket` 只修改用户明确要求变更的字段。
## 决策流程
1. 如果用户想创建已同步 ticket 记录,调用 `create_ticket`
2. 如果用户想把 ticket 关联到 case,视情况先创建 ticket 或先取回已有 ticket row_id,再调用 `attach_ticket_to_case`
3. 如果用户想浏览或对比已同步 ticket,调用 `list_tickets`
4. 如果用户想修改已同步 ticket 字段,调用 `update_ticket`
1. 如果用户想创建已同步 ticket 并关联到 case,调用 `create_ticket(case_id=..., uid=...)`
2. 如果用户想浏览或对比已同步 ticket,调用 `list_tickets`
3. 如果用户想修改已同步 ticket 字段,调用 `update_ticket`
## SOP
@@ -45,7 +42,7 @@ metadata:
1. 从请求中提取最窄且最有用的过滤条件。
2. 调用 `list_tickets`
3. 解析返回的 JSON 字符串。
4. 以紧凑的工作流视图呈现;如果用户大概率下一步要附加或复用该 ticket,则显式展示 ticket row_id。
4. 以紧凑的工作流视图呈现;如果用户大概率下一步要复用该 ticket,则显式展示 ticket row_id。
首选回复结构:
@@ -56,17 +53,10 @@ metadata:
### 创建 Ticket
1. 收集用户想同步的外部 ticket 详情。
2. 调用 `create_ticket`
3. 确认创建后的 ticket row_id。
4. 如果该 ticket 应该关联到 case,建议下一步附加到 case。
### 把 Ticket 附加到 Case
1. 要求提供 `case_id`
2. 如果用户还没有 ticket row_id,则先为新 ticket 调用 `create_ticket`,或先取回已有 ticket
3. 调用 `attach_ticket_to_case(case_id=<case_id>, ticket_row_id=<ticket_row_id>)`
4. 确认 ticket 已附加成功
2. 收集用户想同步的外部 ticket 详情
3. 调用 `create_ticket(case_id=..., uid=..., ...)`
4. 确认创建后的 ticket row_id 及其已关联到指定 case
### 更新 Ticket
@@ -83,9 +73,8 @@ metadata:
## 澄清规则
- 只有当用户要附加到 case 却未提供时,才询问 `case_id`
- 只有当用户创建 ticket 却未提供 case_id 时,才询问 `case_id`
- 只有当用户要更新特定已同步 ticket 却未提供时,才询问 `ticket_id`
- 如果用户想在一次请求中创建并附加 ticket,就完成两步,不要强迫用户拆分工作流。
## 输出规则
+29 -104
View File
@@ -12,9 +12,10 @@ from PLUGINS.SIEM.tools import SIEMToolKit
from PLUGINS.SIRP.nocolymodel import Group, Condition, Operator
from PLUGINS.SIRP.sirpapi import Alert, Artifact, Case, Enrichment, Knowledge, Playbook, Ticket
from PLUGINS.SIRP.sirpbasemodel import AI_PROFILE_MCP
from PLUGINS.SIRP.sirpcoremodel import TicketStatus, TicketType, ArtifactName, ArtifactType, ArtifactRole, ArtifactReputationScore, Severity, AttackStage, Confidence, \
AlertStatus, CaseStatus, CaseVerdict, EnrichmentModel, EnrichmentType, EnrichmentProvider, TicketModel, ArtifactModel
from PLUGINS.SIRP.sirpextramodel import PlaybookType, KnowledgeSource, PlaybookJobStatus
from PLUGINS.SIRP.sirpcoremodel import TicketStatus, TicketType, ArtifactType, ArtifactRole, Severity, AttackStage, \
Confidence, \
AlertStatus, CaseStatus, CaseVerdict, EnrichmentModel, EnrichmentType, EnrichmentProvider, TicketModel
from PLUGINS.SIRP.sirpextramodel import PlaybookType, PlaybookJobStatus
def _dump_models_for_ai(models, limit: int) -> list[dict]:
@@ -58,6 +59,7 @@ def list_cases(
title: Annotated[Optional[str], Field(description="Fuzzy case title filter (Case 标题模糊过滤)")] = None,
tags: Annotated[Optional[list[str]], Field(description="Case tag filter (Case 标签过滤)")] = None,
lazy_load: Annotated[bool, Field(description="True means do not load attached related data (True 表示不加载关联数据)")] = True,
include_discussions: Annotated[bool, Field(description="Include case discussions in the result (返回结果中包含讨论记录)")] = True,
limit: Annotated[int, Field(description="Max cases to return (最多返回条数)")] = 10
) -> Annotated[list[dict], Field(description="Matching cases as AI-friendly JSON list (匹配的 Case 列表)")]:
"""List cases with optional filters. (列出 Case,支持多条件过滤)"""
@@ -83,38 +85,34 @@ def list_cases(
filter_model = Group(logic="AND", children=conditions or [])
models = Case.list(filter_model, lazy_load=lazy_load)
return _dump_models_for_ai(models, limit)
results = _dump_models_for_ai(models, limit)
if include_discussions:
for item in results:
row_id_val = item.get("row_id")
if row_id_val:
discussions = Case.get_discussions_by_row_id(row_id_val) or []
item["discussions"] = discussions
def get_case_discussions(
case_id: Annotated[str, Field(description="Case ID, e.g. case_000005 (Case ID)")]
) -> Annotated[Optional[list[str]], Field(
description="Case discussions as JSON string list, or None if case not found (Case 讨论记录 JSON 字符串列表,Case 不存在时返回 None)")]:
"""Get case discussions by case ID. (通过 Case ID 获取讨论记录)"""
discussions = Case.get_discussions_by_id(case_id)
if discussions is None:
return None
return [json.dumps(item, ensure_ascii=False) for item in discussions]
return results
def update_case(
case_id: Annotated[str, Field(description="Case ID to update (待更新的 Case ID)")],
severity_ai: Annotated[Optional[Severity], Field(description="Updated AI-assessed severity (更新 AI 评估严重程度)")] = None,
confidence_ai: Annotated[Optional[Confidence], Field(description="Updated AI-assessed confidence (更新 AI 评估置信度)")] = None,
attack_stage_ai: Annotated[Optional[AttackStage], Field(description="Updated AI-assessed attack stage (更新 AI 评估攻击阶段)")] = None,
comment_ai: Annotated[Optional[str], Field(description="Updated AI comment, Markdown supported (更新 AI 注释,支持 Markdown)")] = None,
verdict_ai: Annotated[Optional[CaseVerdict], Field(description="Updated AI-assessed verdict (更新 AI 评估判定结果)")] = None,
summary_ai: Annotated[Optional[str], Field(description="Updated AI closure summary, Markdown supported (更新 AI 结案摘要,支持 Markdown)")] = None
comment: Annotated[Optional[str], Field(description="Analyst comment, Markdown supported (分析师注释,支持 Markdown)")] = None,
summary: Annotated[Optional[str], Field(description="Closure summary, Markdown supported (结案摘要,支持 Markdown)")] = None,
) -> Annotated[Optional[str], Field(description="Updated case row ID, or None if not found (更新后的 Case 行 ID,不存在时返回 None)")]:
"""Update AI-assessed fields on a case. (更新 Case AI 评估字段)"""
"""Update case fields including AI-assessed fields, analyst comment, and closure summary. (更新 Case 字段,包括 AI 评估字段、分析师注释和结案摘要)"""
return Case.update_by_id(
case_id=case_id,
severity_ai=severity_ai,
confidence_ai=confidence_ai,
attack_stage_ai=attack_stage_ai,
comment_ai=comment_ai,
verdict_ai=verdict_ai,
summary_ai=summary_ai
comment=comment,
summary=summary,
)
@@ -150,40 +148,6 @@ def list_alerts(
return _dump_models_for_ai(models, limit)
def get_alert_discussions(
alert_id: Annotated[str, Field(description="Alert ID, e.g. alert_000001 (Alert ID)")]
) -> Annotated[Optional[list[str]], Field(
description="Alert discussions as JSON string list, or None if alert not found (Alert 讨论记录 JSON 字符串列表,Alert 不存在时返回 None)")]:
"""Get alert discussions by alert ID. (通过 Alert ID 获取讨论记录)"""
discussions = Alert.get_discussions(alert_id)
if discussions is None:
return None
return [json.dumps(item, ensure_ascii=False) for item in discussions]
# Artifact
# Do not open to mcp , because we think artifact is add only by automation, not human
def create_artifact(
name: Annotated[ArtifactName, Field(description="Artifact name (实体名称)")] = ArtifactName.UNKNOWN,
type: Annotated[Optional[ArtifactType], Field(description="Artifact type (实体类型)")] = None,
role: Annotated[Optional[ArtifactRole], Field(description="Artifact role in event (实体在事件中的角色)")] = None,
owner: Annotated[str, Field(description="Owning system or user (所属系统或用户)")] = "",
value: Annotated[str, Field(description="Artifact value (实体值)")] = "",
reputation_provider: Annotated[str, Field(description="Threat intel provider (威胁情报提供商)")] = "",
reputation_score: Annotated[Optional[ArtifactReputationScore], Field(description="Artifact reputation score (实体信誉评分)")] = None
) -> Annotated[str, Field(description="Created artifact record row ID (创建的 Artifact 行 ID)")]:
"""Create one artifact record. (创建一条 Artifact 记录)"""
model = ArtifactModel()
model.name = name
model.type = type
model.role = role
model.owner = owner
model.value = value
model.reputation_provider = reputation_provider
model.reputation_score = reputation_score
return Artifact.create(model)
# Do not open to mcp , because we think artifact is add only by automation, not human
def attach_artifact_to_alert(
alert_id: Annotated[str, Field(description="Target alert ID to receive the existing artifact (接收 Artifact 的目标 Alert ID)")],
@@ -202,7 +166,6 @@ def list_artifacts(
artifact_id: Annotated[Optional[str], Field(description="Artifact ID filter, e.g. artifact_000001 (Artifact ID 过滤)")] = None,
type: Annotated[Optional[list[ArtifactType]], Field(description="Artifact type filter (实体类型过滤)")] = None,
role: Annotated[Optional[list[ArtifactRole]], Field(description="Artifact role filter (实体角色过滤)")] = None,
reputation_score: Annotated[Optional[list[ArtifactReputationScore]], Field(description="Artifact reputation filter (实体信誉过滤)")] = None,
owner: Annotated[Optional[str], Field(description="Artifact owner filter, exact match (实体所有者过滤,精确匹配)")] = None,
value: Annotated[Optional[str], Field(description="Artifact value filter, exact match (实体值过滤,精确匹配)")] = None,
lazy_load: Annotated[bool, Field(description="True means do not load attached related data (True 表示不加载关联数据)")] = True,
@@ -218,8 +181,6 @@ def list_artifacts(
conditions.append(Condition(field="type", operator=Operator.IN, value=type))
if role:
conditions.append(Condition(field="role", operator=Operator.IN, value=role))
if reputation_score:
conditions.append(Condition(field="reputation_score", operator=Operator.IN, value=reputation_score))
if owner:
conditions.append(Condition(field="owner", operator=Operator.EQ, value=owner))
if value:
@@ -285,31 +246,23 @@ def attach_enrichment_to_target(
# Ticket
def create_ticket(
case_id: Annotated[str, Field(description="Target case ID to attach the ticket to (挂载 Ticket 的目标 Case ID)")],
uid: Annotated[str, Field(description="External ticket ID to sync into SIRP (同步到 SIRP 的外部工单 ID)")],
title: Annotated[str, Field(description="Ticket title (工单标题)")] = "",
status: Annotated[Optional[TicketStatus], Field(description="External ticket status (外部工单状态)")] = None,
type: Annotated[Optional[TicketType], Field(description="External ticket type (外部工单类型)")] = None,
src_url: Annotated[str, Field(description="External ticket URL (外部工单 URL)")] = ""
) -> Annotated[str, Field(description="Created ticket record row ID (创建的 Ticket 行 ID)")]:
"""Create one synced external ticket record in SIRP. (在 SIRP 中创建一条同步的外部工单记录)"""
"""Create one synced external ticket record in SIRP and attach it to a case. (在 SIRP 中创建一条同步的外部工单记录并挂载到 Case)"""
model = TicketModel()
model.uid = uid
model.title = title
model.status = status
model.type = type
model.src_url = src_url
return Ticket.create(model)
def attach_ticket_to_case(
case_id: Annotated[str, Field(description="Target case ID to receive the existing ticket (接收 Ticket 的目标 Case ID)")],
ticket_row_id: Annotated[str, Field(description="Ticket record row ID returned by create_ticket (由 create_ticket 返回的 Ticket 行 ID)")]
) -> Annotated[Optional[str], Field(description="Attached ticket record row ID, or None if case not found (挂载后的 Ticket 行 ID,Case 不存在时返回 None)")]:
"""Attach one existing ticket record to an existing case. (将已有 Ticket 挂载到已有 Case)"""
return Case.attach_ticket(
case_id=case_id,
ticket_row_id=ticket_row_id
)
ticket_row_id = Ticket.create(model)
Case.attach_ticket(case_id=case_id, ticket_row_id=ticket_row_id)
return ticket_row_id
def list_tickets(
@@ -411,38 +364,12 @@ def execute_playbook(
return result.model_dump_json_for_ai(profile=AI_PROFILE_MCP)
def list_knowledge(
row_id: Annotated[Optional[str], Field(description="Knowledge row ID filter, e.g. 03c26478-b213-44c8-b651-3cc88abaac01 (知识条目行 ID 过滤)")] = None,
source: Annotated[Optional[list[KnowledgeSource]], Field(description="Knowledge source filter (知识来源过滤)")] = None,
title: Annotated[Optional[str], Field(description="Fuzzy knowledge title filter (知识标题模糊过滤)")] = None,
body: Annotated[Optional[str], Field(description="Fuzzy knowledge body filter (知识内容模糊过滤)")] = None,
tags: Annotated[Optional[list[str]], Field(description="Knowledge tag filter (知识标签过滤)")] = None,
limit: Annotated[int, Field(description="Max knowledge records to return (最多返回条数)")] = 10
) -> Annotated[list[dict], Field(description="Matching knowledge records as AI-friendly JSON list (匹配的知识条目列表)")]:
"""List knowledge records with optional filters. (列出知识条目,支持多条件过滤)"""
conditions = []
if row_id:
conditions.append(Condition(field="rowId", operator=Operator.EQ, value=row_id))
if source:
conditions.append(Condition(field="source", operator=Operator.IN, value=source))
if title:
conditions.append(Condition(field="title", operator=Operator.CONTAINS, value=title))
if body:
conditions.append(Condition(field="body", operator=Operator.CONTAINS, value=body))
if tags:
conditions.append(Condition(field="tags", operator=Operator.CONTAINS, value=tags))
filter_model = Group(logic="AND", children=conditions or [])
models = Knowledge.list(filter_model, lazy_load=True)
return _dump_models_for_ai(models, limit)
def update_knowledge(
knowledge_id: Annotated[str, Field(description="Knowledge ID to update (待更新的知识条目 ID)")],
title: Annotated[Optional[str], Field(description="Updated knowledge title (更新知识标题)")] = None,
body: Annotated[Optional[str], Field(description="Updated knowledge body (更新知识内容)")] = None,
expires_at: Annotated[Optional[str], Field(description="Updated expiration time; omit or keep empty for permanently valid knowledge (更新过期时间,不填写表示永久有效)")] = None,
expires_at: Annotated[Optional[str], Field(
description="Updated expiration time; omit or keep empty for permanently valid knowledge (更新过期时间,不填写表示永久有效)")] = None,
tags: Annotated[Optional[list[str]], Field(description="Updated knowledge tags; pass [] to clear (更新知识标签,传 [] 可清空)")] = None
) -> Annotated[Optional[str], Field(description="Updated knowledge row ID, or None if not found (更新后的知识条目行 ID,不存在时返回 None)")]:
"""Update one knowledge record in SIRP. (更新 SIRP 中一条知识条目)"""
@@ -456,9 +383,11 @@ def update_knowledge(
def search_knowledge(
keyword: Annotated[Union[str, list[str]], Field(description="Search keyword or keyword list; when a list is provided, records matching at least one item are returned (搜索关键词或关键词列表;传入列表时返回匹配至少一个列表项的记录)")],
keyword: Annotated[Union[str, list[str]], Field(
description="Search keyword or keyword list; when a list is provided, records matching at least one item are returned (搜索关键词或关键词列表;传入列表时返回匹配至少一个列表项的记录)")],
limit: Annotated[int, Field(description="Maximum number of knowledge records to return (最多返回的知识记录数量)")] = 10
) -> Annotated[str, Field(description="Relevant knowledge entries, policies, and special handling instructions as a JSON list string (JSON 列表字符串形式的相关知识条目、策略及特殊处理说明)")]:
) -> Annotated[str, Field(
description="Relevant knowledge entries, policies, and special handling instructions as a JSON list string (JSON 列表字符串形式的相关知识条目、策略及特殊处理说明)")]:
"""Search the internal knowledge base by keyword. (按关键词搜索内部知识库)"""
results = Knowledge.search(keyword, limit=limit)
return results
@@ -536,12 +465,10 @@ REGISTERED_MCP_TOOLS = [
# case
list_cases,
get_case_discussions,
update_case,
# alert
list_alerts,
get_alert_discussions,
# artifact
list_artifacts,
@@ -556,7 +483,6 @@ REGISTERED_MCP_TOOLS = [
list_playbook_runs,
# knowledge
list_knowledge,
update_knowledge,
search_knowledge,
@@ -564,7 +490,6 @@ REGISTERED_MCP_TOOLS = [
list_tickets,
create_ticket,
update_ticket,
attach_ticket_to_case,
# SIEM
get_current_time,
+6 -9
View File
@@ -769,12 +769,11 @@ class Case(BaseWorksheetEntity[CaseModel]):
severity: Union[Severity, None] = None,
status=None,
verdict=None,
comment: Union[str, None] = None,
summary: Union[str, None] = None,
severity_ai: Union[Severity, None] = None,
confidence_ai: Union[Confidence, None] = None,
attack_stage_ai=None,
comment_ai: Union[str, None] = None,
verdict_ai=None,
summary_ai: Union[str, None] = None
) -> Union[str, None]:
case_old = cls.get_by_id(case_id, lazy_load=True)
if not case_old:
@@ -788,18 +787,16 @@ class Case(BaseWorksheetEntity[CaseModel]):
case_new.status = status
if verdict is not None:
case_new.verdict = verdict
if comment is not None:
case_new.comment = comment
if summary is not None:
case_new.summary = summary
if severity_ai is not None:
case_new.severity_ai = severity_ai
if confidence_ai is not None:
case_new.confidence_ai = confidence_ai
if attack_stage_ai is not None:
case_new.attack_stage_ai = attack_stage_ai
if comment_ai is not None:
case_new.comment_ai = comment_ai
if verdict_ai is not None:
case_new.verdict_ai = verdict_ai
if summary_ai is not None:
case_new.summary_ai = summary_ai
return cls.update(case_new)