Status: RFC๏ผRequest for Comments๏ผ Created: 2026-02-15 Scope: Pipeline ๆไน ๅใMCP ๆชๆกๅณ่ผธใๆ็จๆๅฐ
็ฎๅ Pipeline ็ณป็ตฑ๏ผv0.4.0๏ผๆฏๅฎๅ จ็ก็ๆ ็๏ผ
Agent ๅณๅ
ฅ YAML/JSON โ _parse_pipeline_config() โ PipelineExecutor โ ็ตๆ โ ไธๆฃ้
็ฝฎ
| ้ขๅ | ็พ็ | ๆๆ |
|---|---|---|
| Pipeline ้ ็ฝฎ | ๆฏๆฌก้่ฆ inline ๅณๅ ฅ | ๅฏไฟๅญใๅฝๅใ้่คไฝฟ็จ |
| ๅท่ก็ตๆ | ๅ ไธๆฌกๆงๅๅณ | ๅฏๆฏๅฐๆญทๅฒ็ตๆใ่ฟฝ่นค่ฎๅ |
| ๆ็จๅท่ก | ไธๅญๅจ | ๅฎๆ่ชๅๆๅฐใๆฐๆ็ป้็ฅ |
| ๅค้จ่ผธๅ ฅ | ๅ
pipeline ๅๆธๅญไธฒ |
ๅฏ่ผๅ ฅๆชๆกใURL |
| MCP ๆชๆกไบคๆ | ็ก | Agent ๅฏ่ฎๅฏซ pipeline ๆชๆก |
- ็ ็ฉถ่ A๏ผใๆๆฏ้ฑ้ฝๆๅฐๅๆจฃ็ PICO ๅ้ก๏ผremimazolam vs propofol in ICU๏ผ๏ผ่ฝไธ่ฝๅญไธไพไธ้ต้่ท๏ผใ
- ็ ็ฉถ่ B๏ผใๆๆณ่จญๅฎๆฏๆ่ชๅๆๅฐ CRISPR gene therapy ็ๆฐๆ็ป๏ผๆๆฐ็ตๆๆ้็ฅๆใใ
- Lab PI๏ผใๆๆ 5 ๅไธๅไธป้ก็ๆๅฐ็ญ็ฅ๏ผๆณๅไบซ็ตฆๅญธ็ไฝฟ็จใใ
- ็ณป็ตฑ็ฎก็ๅก๏ผใ้่ฆไธๅ cron-like ๆฉๅถๅฎๆๆดๆฐๆ็ป่ณๆๅบซใใ
MCP ่ฆ็ฏๅฎ็พฉไบ Resources โ server ๆด้ฒ็ตฆ client ็ๅฏ่ฎ่ณๆ๏ผ
| ่ฝๅ | ๆฏๆด | ๅ่จป |
|---|---|---|
้ๆ
Resource (@mcp.resource) |
โ | ๅบๅฎ URI๏ผๅฆ pubmed://filters/all |
Resource Template (@mcp.resource("uri/{param}")) |
โ | ๅๆ URI๏ผserver ็ซฏๅฏ่ฎๆชๅๅณ |
| Client ่ฎๅ Resource | โ | resources/read method |
| Client ๅฏซๅ ฅ Resource | โ | MCP ่ฆ็ฏ็กๅฏซๅ ฅ API |
| Resource ่จ้ฑ/่ฎๆด้็ฅ | โ | resources/subscribe + notifications/resources/updated |
| Binary content (้ๆๅญ) | โ | ๅฏ็จ BlobResourceContents ๅๅณ base64 |
้้ต้ๅถ๏ผMCP Resources ๆฏๅฏ่ฎ็ใClient๏ผAgent๏ผ็กๆณ้้ MCP ็ดๆฅใไธๅณๆชๆกใๆใๅฏซๅ ฅ Resourceใใ
MCP 2025-03-26 ่ฆ็ฏๆฐๅข Sampling ่ฝๅ๏ผไฝ้ๆฏ server ่ซๆฑ client ๅ LLM ๆจ็๏ผ้ๆชๆกไบคๆใ
| ๆนๆณ | ๆนๅ | ๅฏฆไฝ |
|---|---|---|
| Tool ๅๆธ๏ผinline๏ผ | Agent โ Server | ็พ่กๆนๅผ๏ผpipeline="yaml: ..." |
| Tool ๅๆธ๏ผfile path๏ผ | Agent โ Server | Agent ๆไพๆฌๅฐ่ทฏๅพ๏ผserver ่ฎๅ |
| Tool ๅๆธ๏ผURL๏ผ | Agent โ Server | Agent ๆไพ URL๏ผserver httpx.get() |
| Resource Template | Server โ Agent | pipeline://saved/{name} ๅๆ
่ฎๅ |
| Tool ๅๅณๅผ | Server โ Agent | ๅๅณ YAML/JSON ๆๅญๆๆชๆก่ทฏๅพ |
| Notification | Server โ Agent | resources/updated ้็ฅๆฐ็ตๆ |
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Presentation Layer (MCP) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ MCP Tools โ โ
โ โ โข save_pipeline(name, config) โ ไฟๅญ โ โ
โ โ โข list_pipelines() โ ๅ่ โ โ
โ โ โข load_pipeline(name|url|path) โ ่ผๅ
ฅ โ โ
โ โ โข delete_pipeline(name) โ ๅช้ค โ โ
โ โ โข schedule_pipeline(name, cron) โ ๆ็จ โ โ
โ โ โข list_schedules() โ ๅ่ๆ็จ โ โ
โ โ โข get_pipeline_history(name) โ ๅท่กๆญทๅฒ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ MCP Resources (ๅฏ่ฎ) โ โ
โ โ โข pipeline://saved/{name} โ ่ฎๅๅทฒๅญ pipeline โ โ
โ โ โข pipeline://templates/{name} โ ๆจกๆฟๅ่ โ โ
โ โ โข pipeline://history/{name}/latest โ ๆๆฐๅท่ก็ตๆ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ MCP Notifications โ โ
โ โ โข resources/updated โ Pipeline ็ตๆๆดๆฐ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Application Layer โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ PipelineStore โ โ
โ โ โข save(name, config, scope) โ PipelineMeta โ โ
โ โ โข load(name) โ PipelineConfig (workspace โ global) โ โ
โ โ โข load_from_url(url) โ PipelineConfig โ โ
โ โ โข load_from_path(path) โ PipelineConfig โ โ
โ โ โข list(scope?) โ list[PipelineMeta] โ โ
โ โ โข delete(name) โ โ
โ โ โข get_history(name, limit) โ list[PipelineRun] โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ PipelineScheduler โ โ
โ โ โข schedule(name, cron_expr) โ ScheduleEntry โ โ
โ โ โข unschedule(name) โ โ
โ โ โข list_schedules() โ list[ScheduleEntry] โ โ
โ โ โข _tick() โ ๆชขๆฅๅฐๆไปปๅ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ PipelineExecutor (ๅทฒๆ) โ โ
โ โ + run_and_store(config) โ ๅท่ก + ๅฒๅญ็ตๆ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Domain Layer โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Entities โ โ
โ โ โข PipelineConfig (ๅทฒๆ) โ โ
โ โ โข PipelineMeta(name, created, updated, tags, hash) โ โ
โ โ โข PipelineRun(id, pipeline_name, started, finished, โ โ
โ โ status, article_count, result_summary) โ โ
โ โ โข ScheduleEntry(pipeline_name, cron, next_run, โ โ
โ โ enabled, last_run, last_status) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Infrastructure Layer โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ File Storage โ โ
โ โ Workspace scope (ๅชๅ
): โ โ
โ โ โข {workspace}/.pubmed-search/pipelines/{name}.yaml โ โ
โ โ โข {workspace}/.pubmed-search/pipeline_runs/{name}/ โ โ
โ โ Global scope (fallback): โ โ
โ โ โข ~/.pubmed-search-mcp/pipelines/{name}.yaml โ โ
โ โ โข ~/.pubmed-search-mcp/pipelines/_index.json โ โ
โ โ โข ~/.pubmed-search-mcp/pipeline_runs/{name}/ โ โ
โ โ โโโ {run_id}.json โ โ
โ โ โข ~/.pubmed-search-mcp/schedules.json โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๆฑบ็ญ D7๏ผๆก็จ workspace + global ้ๅฑคๅฒๅญใ Workspace scope ๅฏ git ่ฟฝ่นคใๅไบซ็ตฆๅไฝ่ ๏ผGlobal scope ่ทจๅฐๆกๅ ฑ็จใ
load(name) โ ๅ
ๆฅ workspace/.pubmed-search/pipelines/{name}.yaml
โ ๆฒๆพๅฐ โ ๆฅ ~/.pubmed-search-mcp/pipelines/{name}.yaml
โ ้ฝๆฒๆ โ 404 error
{workspace}/ # VS Code workspace root
โโโ .pubmed-search/ # ๅฐๆก็ด่จญๅฎ็ฎ้
โโโ pipelines/ # Pipeline ๅฒๅญ
โ โโโ weekly_remimazolam.yaml # ๅทฒๅญ pipeline
โ โโโ pico_icu_sedation.yaml
โโโ pipeline_runs/ # ๅท่กๆญทๅฒ๏ผๅปบ่ญฐ .gitignore๏ผ
โโโ weekly_remimazolam/
โโโ 20260215_143022.json
~/.pubmed-search-mcp/ # data_dir (็พๆ)
โโโ sessions.json # ็พๆ
โโโ article_cache.json # ็พๆ
โโโ pipelines/ # ๆฐๅข๏ผPipeline ๅฒๅญ
โ โโโ _index.json # ็ดขๅผ๏ผ{name โ PipelineMeta}
โ โโโ weekly_remimazolam.yaml # ๅทฒๅญ pipeline
โ โโโ crispr_monthly.yaml
โโโ pipeline_runs/ # ๆฐๅข๏ผๅท่กๆญทๅฒ
โ โโโ weekly_remimazolam/
โ โ โโโ 20260215_143022.json # ๆฏๆฌกๅท่ก็็ตๆๆ่ฆ
โ โ โโโ 20260222_143015.json
โ โโโ pico_icu_sedation/
โ โโโ 20260215_090000.json
โโโ schedules.json # ๆฐๅข๏ผๆ็จ่จญๅฎ
| ๆไฝ | ้ ่จญ scope | ๅฏ่ฆๅฏซ |
|---|---|---|
save_pipeline |
workspace๏ผ่ฅๆ๏ผโ global | scope ๅๆธ |
load_pipeline |
workspace โ global fallback | ่ชๅ |
list_pipelines |
ๅไฝต้กฏ็คบ๏ผๆจ่จป scope๏ผ | scope ๅๆธ้ๆฟพ |
delete_pipeline |
็ฒพ็ขบๅน้ ๏ผๅ workspace๏ผ | ่ชๅ |
get_pipeline_history |
่ท้จ pipeline ๆๅจ scope | ่ชๅ |
schedule_pipeline |
ๅ global๏ผๆ็จ้่ทจ workspace๏ผ | ๅบๅฎ |
# ~/.pubmed-search-mcp/pipelines/weekly_remimazolam.yaml
# ่ unified_search pipeline ๅๆธๆ ผๅผๅฎๅ
จ็ธๅฎน
name: "Weekly Remimazolam Review"
tags: [anesthesia, sedation, remimazolam]
# ๆนๅผไธ๏ผไฝฟ็จๆจกๆฟ
template: comprehensive
template_params:
query: "remimazolam sedation ICU"
sources: "pubmed,openalex,europe_pmc"
limit: 30
min_year: 2024
# ๆนๅผไบ๏ผ่ชๅฎ็พฉ steps๏ผ่็พ่กๆ ผๅผๅฎๅ
จไธ่ด๏ผ
# steps:
# - id: s1
# action: search
# params: { query: "remimazolam", sources: "pubmed", limit: 20 }
# ...
output:
format: markdown
limit: 20
ranking: quality
# ๆไน
ๅ็นๆๆฌไฝ๏ผไธๅฝฑ้ฟๅท่ก๏ผ
schedule:
cron: "0 9 * * 1" # ๆฏ้ฑไธ 09:00
enabled: true
notify: true # ๆๆฐ็ตๆๆ้็ฅ
diff_mode: true # ๅช้กฏ็คบ่ไธๆฌกไธๅ็็ตๆ@mcp.tool()
async def save_pipeline(
name: str,
config: str, # YAML/JSON ๅญไธฒ๏ผ่ unified_search pipeline ๅๆธ่ฆๆ ผ็ธๅ๏ผ
tags: str = "", # ้่ๅ้ๆจ็ฑค
description: str = "",
) -> str:
"""Save a pipeline configuration for reuse.
The config format is identical to unified_search's pipeline parameter.
Saved pipelines can be loaded by name in unified_search:
unified_search(pipeline="saved:weekly_remimazolam")
"""
@mcp.tool()
async def list_pipelines(
tag: str = "", # ๆๆจ็ฑค้ๆฟพ
) -> str:
"""List all saved pipeline configurations."""
@mcp.tool()
async def load_pipeline(
source: str, # name | file:///path | https://url | saved:name
) -> str:
"""Load a pipeline from name, file path, or URL.
Supports:
- Saved pipeline: "weekly_remimazolam" or "saved:weekly_remimazolam"
- Local file: "file:///path/to/pipeline.yaml"
- URL: "https://example.com/pipelines/my_search.yaml"
Returns the pipeline YAML for review before execution.
To execute, pass the result to unified_search(pipeline=...).
"""
@mcp.tool()
async def delete_pipeline(name: str) -> str:
"""Delete a saved pipeline configuration."""@mcp.tool()
async def schedule_pipeline(
name: str, # ๅทฒๅญ pipeline ๅ็จฑ
cron: str = "", # cron ่กจ้ๅผ๏ผ็ฉบ = ๅๆญขๆ็จ๏ผ
diff_mode: bool = True, # ๅช้กฏ็คบๆฐๅขๆ็ซ
notify: bool = True, # ๆ็ตๆๆ้็ฅ
) -> str:
"""Schedule a saved pipeline for periodic execution.
Cron format: "minute hour day month weekday"
Examples:
- "0 9 * * 1" โ Every Monday 9:00 AM
- "0 0 1 * *" โ First day of each month
- "0 */6 * * *" โ Every 6 hours
- "" โ Remove schedule
"""
@mcp.tool()
async def list_schedules() -> str:
"""List all scheduled pipeline executions with next run times."""
@mcp.tool()
async def get_pipeline_history(
name: str,
limit: int = 5,
) -> str:
"""Get execution history for a saved pipeline.
Shows: date, article count, new articles vs. previous run, status.
"""# ็พๆ็ pipeline ๅๆธๆดๅฑๆฏๆด saved: ๅ url: ๅ็ถด
async def unified_search(
...,
pipeline: str | None = None,
# ๆฐๅขๆฏๆดๆ ผๅผ๏ผ
# "saved:weekly_remimazolam" โ ๅพๆไน
ๅ่ผๅ
ฅ
# "url:https://example.com/p.yaml" โ ๅพ URL ่ผๅ
ฅ
# "file:///path/to/p.yaml" โ ๅพๆฌๅฐ่ผๅ
ฅ๏ผ้่ฆ Agent ๆ fs ๅญๅ๏ผ
# ๅๆ inline YAML/JSON ไป็ถๆฏๆด
):@mcp.resource("pipeline://saved/{name}")
async def get_saved_pipeline(name: str) -> str:
"""Read a saved pipeline configuration."""
store = _get_pipeline_store()
config = store.load(name)
return yaml.dump(dataclasses.asdict(config))
@mcp.resource("pipeline://templates/{name}")
async def get_template_info(name: str) -> str:
"""Read template reference with parameters and example."""
entry = PIPELINE_TEMPLATES.get(name)
return json.dumps(entry, indent=2)
@mcp.resource("pipeline://history/{name}/latest")
async def get_latest_run(name: str) -> str:
"""Read the latest execution result for a pipeline."""
store = _get_pipeline_store()
run = store.get_latest_run(name)
return json.dumps(dataclasses.asdict(run))| ๆนๆก | ๅช้ป | ็ผบ้ป |
|---|---|---|
| A. ๅ งๅปบ asyncio scheduler | ้ถไพ่ณดใ่ MCP server ๅ็ๅฝ้ฑๆ | ้ server ๆ็บ้่กใ้ๅๅคฑๅปๆ็จ |
| B. OS cron + CLI entrypoint | ็ฉฉๅฎใ็ณป็ตฑ็ดๆ็จ | ้่ฆ้กๅค CLIใ็กๆณๅๆ ็ฎก็ |
| C. APScheduler | ๅ่ฝๅฎๆดใๆฏๆด persistence | ๆฐไพ่ณดใๅฏ่ฝ้ๅบฆ่จญ่จ |
| D. ๆททๅๆนๆก๏ผๅ งๅปบ tick + JSON state | ่ผ้ใๅฏๆขๅพฉ | ็ฒพๅบฆๅ้ๆผ tick ้้ |
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MCP Server Lifespan โ
โ โ
โ startup: โ
โ load schedules.json โ
โ start _tick_loop() โ
โ โ
โ _tick_loop (ๆฏ60็ง): โ
โ for schedule in sched: โ
โ if should_run(): โ
โ asyncio.create_ โ
โ task(execute()) โ
โ update next_run โ
โ โ
โ shutdown: โ
โ save schedules.json โ
โ cancel background โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๆ ธๅฟ้่ผฏ๏ผ
class PipelineScheduler:
def __init__(self, store: PipelineStore, executor: PipelineExecutor):
self._store = store
self._executor = executor
self._schedules: dict[str, ScheduleEntry] = {}
self._task: asyncio.Task | None = None
async def start(self):
"""Load schedules and start tick loop."""
self._schedules = self._load_schedules()
self._task = asyncio.create_task(self._tick_loop())
async def _tick_loop(self):
"""Check every 60 seconds for due pipelines."""
while True:
await asyncio.sleep(60)
now = datetime.now(UTC)
for name, entry in self._schedules.items():
if entry.enabled and entry.next_run <= now:
asyncio.create_task(self._execute_scheduled(name, entry))
entry.next_run = self._compute_next_run(entry.cron)
self._save_schedules()
async def _execute_scheduled(self, name: str, entry: ScheduleEntry):
"""Execute a scheduled pipeline and store results."""
config = self._store.load(name)
results = await self._executor.execute(config)
run = PipelineRun(
pipeline_name=name,
started=datetime.now(UTC),
article_count=len(results.articles),
# diff with previous run...
)
self._store.save_run(name, run)
# Notify via MCP resource update
if entry.notify:
await self._notify_resource_updated(name)def compute_diff(current: list[str], previous: list[str]) -> PipelineDiff:
"""Compare PMID lists between runs.
Returns:
new_pmids: ๆฌๆฌกๆฐๅบ็พ็
removed_pmids: ไธๆฌกๆไฝๆฌๆฌกๆฒๆ็
unchanged_count: ๅ
ฉๆฌก้ฝๆ็
"""
current_set = set(current)
previous_set = set(previous)
return PipelineDiff(
new_pmids=sorted(current_set - previous_set),
removed_pmids=sorted(previous_set - current_set),
unchanged_count=len(current_set & previous_set),
)| ้ขจ้ช | ็ทฉ่งฃๆชๆฝ |
|---|---|
| URL ่ณๆบๆณจๅ ฅ๏ผSSRF๏ผ | ็ฝๅๅฎๅๅ๏ผgithub.com, gist.github.com, raw.githubusercontent.com |
| ๆฌๅฐ่ทฏๅพ็ฉฟ่ถ | ้ๅถๅจ data_dir ไธ๏ผ็ฆๆญข .. ๅ็ฌฆ่้ฃ็ต |
| ๆ็จ่ณๆบ่็ก | ๆๅฐ้้ 1 ๅฐๆใๅๆๆๅค 5 ๅๆ็จใๅท่ก่ถ ๆ 5 ๅ้ |
| ็ฃ็ข็ฉบ้ | ๆฏๅ pipeline ๆๅคไฟ็ 100 ๆฌกๅท่กๆญทๅฒ |
| API Rate Limit | ๆ็จๅท่กไฝฟ็จ้็ดๆจกๅผ๏ผๆธๅฐ sourcesใ้ไฝ limit๏ผ |
้ ไผฐ๏ผ2-3 ๅๅทฅไฝๆฅ
| ไปปๅ | ๅฑค | ๆชๆก |
|---|---|---|
PipelineMeta, PipelineRun entities |
Domain | domain/entities/pipeline.py |
PipelineStore (save/load/list/delete) |
Application | application/pipeline/store.py |
save_pipeline, list_pipelines, load_pipeline, delete_pipeline tools |
Presentation | tools/pipeline_tools.py |
Resource templates pipeline://saved/{name} |
Presentation | resources.py |
| DI Container ๆดๅ | Infrastructure | container.py |
| Tests | Tests | tests/test_pipeline_store.py |
ๆญค้ๆฎตๅฎๆๅพ๏ผAgent ๅฏไปฅๅญ/ๅ/ๅ่ pipeline ้
็ฝฎ๏ผunified_search(pipeline="saved:xxx") ๅฏ่ผๅ
ฅๅทฒๅญๆนๆกใ
้ ไผฐ๏ผ1 ๅๅทฅไฝๆฅ
| ไปปๅ | ๅฑค | ๅ่จป |
|---|---|---|
load_from_url(url) |
Application | httpx.get() + YAML/JSON parse + ๅๅ็ฝๅๅฎ |
load_from_path(path) |
Application | pathlib.Path.read_text() + ่ทฏๅพ้ฉ่ญ |
unified_search pipeline ๅๆธๆฏๆด url: file: ๅ็ถด |
Presentation | ่ทฏ็ฑๅฐ store |
| ๅฎๅ จๆธฌ่ฉฆ | Tests | SSRF ้ฒ่ญทใ่ทฏๅพ็ฉฟ่ถๆธฌ่ฉฆ |
้ ไผฐ๏ผ1-2 ๅๅทฅไฝๆฅ
| ไปปๅ | ๅฑค | ๅ่จป |
|---|---|---|
PipelineRun ๆไน
ๅ |
Application | pipeline_runs/{name}/{run_id}.json |
| Diff ่จ็ฎ | Application | PMID ้ๅๅทฎ็ฐ |
get_pipeline_history tool |
Presentation | ้กฏ็คบๆญทๅฒ + diff |
Resource pipeline://history/{name}/latest |
Presentation | ๆๆฐ็ตๆ |
้ ไผฐ๏ผ2-3 ๅๅทฅไฝๆฅ๏ผๆ่ค้๏ผ
| ไปปๅ | ๅฑค | ๅ่จป |
|---|---|---|
ScheduleEntry entity |
Domain | cron + state |
PipelineScheduler |
Application | tick loop + execute + notify |
| Lifespan ๆดๅ | Presentation | startup/shutdown |
schedule_pipeline, list_schedules tools |
Presentation | |
Cron ่งฃๆ๏ผcroniter ๆ่ชๅฏฆไฝ๏ผ |
Infrastructure | ่ผ้ cron parser |
| Resource ๆดๆฐ้็ฅ | Presentation | resources/updated |
่ฅๆ็จ่ค้ๅบฆ้้ซ๏ผๅฏๆน็จ OS ็ดๆนๆก๏ผ
# ไฝฟ็จ่
่ช่ก่จญๅฎ OS cron / Windows Task Scheduler
# ๆไพ CLI entrypoint ่ฎ cron ๅผๅซ
# crontab -e
0 9 * * 1 pubmed-search run-pipeline weekly_remimazolam --diff
# ๆ Windows Task Scheduler
schtasks /create /tn "WeeklyPubMed" /tr "uv run python -m pubmed_search.cli run-pipeline weekly_remimazolam" /sc weekly /d MON /st 09:00ๆญคๆนๆก้ไฝๅ ง้จ่ค้ๅบฆ๏ผไฝไฝฟ็จ่ ้ซ้ฉ่ผๅทฎ๏ผ้ๆๅ่จญๅฎ OS ๆ็จ๏ผใ
ไธๆฐๅข MCP tools๏ผๅ
ๆดๅฑ unified_search ็ pipeline ๅๆธ๏ผ
# ็ดๆฅๅจ pipeline ๅๆธไธญๆฏๆดๆๆๆไฝ
unified_search(pipeline="save:my_plan") # ไฟๅญ็ถๅๆๅฐ
unified_search(pipeline="saved:my_plan") # ่ผๅ
ฅๅทฒๅญ
unified_search(pipeline="url:https://example.com") # ๅพ URL
unified_search(pipeline="list") # ๅ่ๅช้ป๏ผไธๅขๅ MCP tool ๆธ้ใ็ผบ้ป๏ผpipeline ๅๆธ่ช็พฉ้่ผใ
-
้็ฅๆฉๅถ๏ผๆ็จๅท่กๅฎๆๅพๅฆไฝ้็ฅ Agent๏ผMCP
resources/updated้่ฆ client ๆ subscription ๆฏๆด๏ผ็ฎๅๅคๆธ MCP client (Claude Desktop, Copilot) ๅฏ่ฝไธๆฏๆดใๆฟไปฃๆนๆก๏ผไธๆฌก Agent ้ฃ็ทๆๅจ session ไธญ้กฏ็คบๆช่ฎ็ตๆใ -
ๅค Server ๅฏฆไพ๏ผDocker ้จ็ฝฒไธญๅฏ่ฝๆๅคๅ server ๅฏๆฌ๏ผๆ็จ้้ฟๅ ้่คๅท่กใๅปบ่ญฐไฝฟ็จ file lock ๆๅ ๅ ่จฑไธๅๅฏฆไพๅ็จๆ็จใ
-
YAML ไพ่ณด๏ผ็ฎๅ pipeline ่งฃๆๅทฒ็ถๅไบ YAML๏ผ
_parse_pipeline_config็จyaml.safe_load๏ผ๏ผไฝpyyamlๆฏ optional dependencyใๆไน ๅๅฒๅญๆ ผๅผๆฏๅฆ็ตฑไธ็จ YAML๏ผๆๅ ่จฑ JSON/YAML ้ๆ ผๅผ๏ผ -
็ตๆๅฒๅญๅฎน้๏ผๆฏๆฌกๅท่ก็็ตๆๆ่ฆๅญๅคๅฐ๏ผๅปบ่ญฐๅญ PMID ๅ่กจ + ๅ 5 ็ฏ metadata๏ผๆจ้ก/ๅนดไปฝ/ๆๅ๏ผ๏ผไธๅญๅฎๆด abstractใ
-
Agent ้ซ้ฉ๏ผๆฐๅข 4-7 ๅ MCP tools ๆฏๅฆๆ้ ๆๅทฅๅ ท้ๅค๏ผ่ๆ ฎๅไฝต็บ 2 ๅๅทฅๅ ท๏ผโ ๅทฒๆฑบ็ญ (D3)๏ผๆก็จ Option B โ 5 ๅ็จ็ซๅทฅๅ ทใ่ฆ Section 10 D3 ่ Section 11 ่ฉณ็ดฐ่จญ่จใpipeline_manage+pipeline_schedule๏ผ็จactionๅๆธๅๅใ
ๅจๅฏฆไฝๅ๏ผ้่ฆๅฐไปฅไธๅ้กๅๅบๆฑบ็ญ๏ผ
| # | ๆฑบ็ญ | ้ธ้ | ๅปบ่ญฐ |
|---|---|---|---|
| D1 | ๅฒๅญๆ ผๅผ | JSON / YAML / ๅ ฉ่ | YAML๏ผไบบ้กๅฏ่ฎ๏ผ่็พ่ก pipeline ๅๆธไธ่ด๏ผ |
| D2 | ๆ็จๅฏฆไฝ | ๅ งๅปบ / OS cron / APScheduler | ๅ งๅปบ tick loop๏ผPhase 4๏ผ |
| D3 | ๅทฅๅ ทๆธ้ | โ Option B: 6 ๅ็จ็ซๅทฅๅ ท | save_pipeline, list_pipelines, load_pipeline, delete_pipeline, get_pipeline_history, schedule_pipelineใ่ฆ Section 11ใ |
| D4 | URL ่ผๅ ฅๅฎๅ จ | ็ฝๅๅฎ / ไปปๆ / ็ฆๆญข | ็ฝๅๅฎ + ไฝฟ็จ่ ๅฏ้ ็ฝฎ |
| D5 | ็ตๆๅฒๅญ | ๅฎๆด/ๆ่ฆ/ๅ PMID | ๆ่ฆ๏ผPMID + top-5 metadata๏ผ |
| D6 | ๆฏๅฆ้่ฆ croniter ไพ่ณด |
ๆฏ/่ชๅฏฆไฝ | ่ฆ่ค้ๅบฆ๏ผ็ฐกๅฎ cron ๅฏ่ชๅฏฆไฝ |
| D7 | ๅฒๅญไฝ็ฝฎ็ฏๅ | โ ้ๅฑค๏ผworkspace + global | workspace ๅชๅ ๏ผๅฏ git ่ฟฝ่นค/ๅไบซ๏ผ๏ผglobal ไฝ็บ่ทจๅฐๆก fallbackใ่ฆ Section 3.2ใ |
ๆฑบ็ญๆฅๆ๏ผ2026-02-15 ๆฑบ็ญ่ ๏ผๅฐๆก็ถญ่ญท่ ็็ฑ๏ผๆฏๅๅทฅๅ ท่ท่ฒฌๅฎไธๆ็ขบ๏ผ็ฌฆๅ MCP ๅทฅๅ ท็่ช็พฉ่จญ่จๅฒๅญธ๏ผไธๅๅทฅๅ ท = ไธๅๅไฝ๏ผใ ็ธ่ผ Option A๏ผ2 ๅๅไฝตๅทฅๅ ท็จ action ๅๆธๅๅ๏ผ่ช็พฉๆดๆธ ๆฐ๏ผAgent ไธ้็่งฃ action ๅญๅฝไปคใ ็ธ่ผ Option C๏ผๅ จๅไฝต้ฒ unified_search๏ผ้ฟๅ unified_search ๅๆธ้่ผใ
| # | ๅทฅๅ ทๅ็จฑ | Phase | ้กๅฅ | ็จ้ |
|---|---|---|---|---|
| 1 | save_pipeline |
1 | pipeline | ไฟๅญ pipeline ้ ็ฝฎ๏ผๆฏๆด scope ้ธๆ๏ผ |
| 2 | list_pipelines |
1 | pipeline | ๅ่ๅทฒๅญ pipeline๏ผๅไฝต workspace + global๏ผ |
| 3 | load_pipeline |
1 | pipeline | ่ผๅ ฅ pipeline๏ผname / URL / path๏ผ |
| 4 | delete_pipeline |
1 | pipeline | ๅช้คๅทฒๅญ pipeline |
| 5 | get_pipeline_history |
3 | pipeline | ๆฅ่ฉขๅท่กๆญทๅฒ่ diff |
| 6 | schedule_pipeline |
4 | pipeline | ๆ็จ / ่งฃ้คๆ็จ / ๆฅ็ๆ็จ |
ๆฐๅข TOOL_CATEGORIES ๆข็ฎ๏ผ
TOOL_CATEGORIES = {
...,
"pipeline": [
save_pipeline,
list_pipelines,
load_pipeline,
delete_pipeline,
get_pipeline_history,
schedule_pipeline,
],
}ๅทฅๅ ท็ธฝๆธ่ฎๅ๏ผ33 โ 39๏ผ+6๏ผ
@mcp.tool()
async def save_pipeline(
name: str,
config: str,
tags: str = "",
description: str = "",
scope: str = "auto",
) -> str:
"""Save a pipeline configuration for later reuse.
The config format is identical to unified_search's pipeline parameter
(YAML or JSON). Saved pipelines can be loaded later by name:
unified_search(pipeline="saved:weekly_remimazolam")
Args:
name: Unique identifier (alphanumeric + hyphens/underscores, max 64 chars).
Overwrites if name already exists (upsert semantics).
config: Pipeline YAML/JSON string. Same format as unified_search pipeline param.
tags: Comma-separated tags for filtering (e.g., "anesthesia,sedation").
description: Human-readable description of the pipeline's purpose.
scope: Storage scope - "workspace" (project-level, git-trackable),
"global" (user-level, cross-project), or "auto" (workspace if
available, otherwise global). Default: "auto".
Returns:
Confirmation with pipeline metadata (name, scope, created/updated timestamp, step count).
"""ๅๅณๆ ผๅผ๏ผ
โ
Pipeline "weekly_remimazolam" saved successfully.
๐ Metadata:
Name: weekly_remimazolam
Description: Weekly remimazolam sedation literature review
Tags: anesthesia, sedation
Steps: 3 (search โ filter โ rank)
Created: 2026-02-15 14:30:22 UTC
Config hash: a1b2c3d4
๐ก Usage:
โข Execute: unified_search(pipeline="saved:weekly_remimazolam")
โข View: load_pipeline(source="weekly_remimazolam")
โข Schedule: schedule_pipeline(name="weekly_remimazolam", cron="0 9 * * 1")
้ฉ่ญ่ฆๅ๏ผ
nameๅน้ ^[a-zA-Z0-9_-]{1,64}$configๅฟ ้ ๆฏๆๆ YAML/JSON ไธๅฏ่งฃๆ็บPipelineConfig- ้่ค
nameๆๅท่ก upsert๏ผๆดๆฐ + ไฟ็ๆญทๅฒ๏ผ
@mcp.tool()
async def list_pipelines(
tag: str = "",
scope: str = "",
) -> str:
"""List all saved pipeline configurations.
Args:
tag: Filter by tag (e.g., "sedation"). Empty = show all.
scope: Filter by scope: "workspace", "global", or "" (show all). Default: "".
Returns:
Table of saved pipelines with name, scope, description, tags, last modified, execution count.
"""ๅๅณๆ ผๅผ๏ผ
๐ฆ Saved Pipelines (3 total, 2 workspace + 1 global):
| Name | Scope | Description | Tags | Modified | Runs |
|------------------------|-----------|--------------------------------------|-----------------------|---------------------|------|
| weekly_remimazolam | workspace | Weekly remimazolam sedation review | anesthesia, sedation | 2026-02-15 14:30 | 12 |
| pico_icu_sedation | workspace | PICO: remimazolam vs propofol in ICU | pico, icu | 2026-02-10 09:00 | 3 |
| crispr_monthly | global | Monthly CRISPR gene therapy update | gene_therapy, crispr | 2026-02-01 00:00 | 5 |
๐ก Load: load_pipeline(source="<name>")
๐ก Execute: unified_search(pipeline="saved:<name>")
@mcp.tool()
async def load_pipeline(
source: str,
) -> str:
"""Load a pipeline configuration for review or editing.
Loads from one of three sources:
- Saved name: "weekly_remimazolam" or "saved:weekly_remimazolam"
- Local file: "file:path/to/pipeline.yaml" (relative to data_dir or absolute)
- URL: "url:https://example.com/pipelines/my_search.yaml"
The returned YAML can be reviewed, modified, and then:
- Executed directly: unified_search(pipeline="<yaml>")
- Saved with changes: save_pipeline(name="...", config="<yaml>")
Args:
source: Pipeline source identifier (see above).
Returns:
Full pipeline YAML content + metadata.
"""ๅๅณๆ ผๅผ๏ผ
๐ Pipeline: weekly_remimazolam
๐ Source: saved (local)
๐
Last modified: 2026-02-15 14:30:22 UTC
---
template: comprehensive
template_params:
query: "remimazolam sedation ICU"
sources: "pubmed,openalex,europe_pmc"
limit: 30
min_year: 2024
output:
format: markdown
limit: 20
ranking: quality
---
๐ก Execute: unified_search(pipeline="saved:weekly_remimazolam")
๐ก Edit & re-save: save_pipeline(name="weekly_remimazolam", config="<modified yaml>")
URL ๅฎๅ จ๏ผ
- ็ฝๅๅฎๅๅ๏ผ
github.com,gist.github.com,raw.githubusercontent.com - ไฝฟ็จ่
ๅฏ้้็ฐๅข่ฎๆธ
PIPELINE_URL_ALLOWLISTๆดๅ - ๅๆ Content-Type ๅฟ ้ ็บ text/plain, text/yaml, application/json, application/yaml
- ๆๅคงไธ่ผๅคงๅฐ๏ผ100 KB
@mcp.tool()
async def delete_pipeline(
name: str,
) -> str:
"""Delete a saved pipeline configuration and its execution history.
Args:
name: Name of the saved pipeline to delete.
Returns:
Confirmation of deletion.
"""ๅๅณๆ ผๅผ๏ผ
๐๏ธ Pipeline "weekly_remimazolam" deleted.
- Configuration removed
- 12 execution history records removed
- Schedule removed (was: every Monday 09:00)
่ก็บ๏ผ
- ๅช้ค
~/.pubmed-search-mcp/pipelines/{name}.yaml - ๅช้ค
~/.pubmed-search-mcp/pipeline_runs/{name}/ๆดๅ็ฎ้ - ่ฅๆๆ็จ๏ผๅๆ็งป้คๆ็จๆข็ฎ
- ไธๅญๅจๆๅๅณ 404 ่ช็พฉ้ฏ่ชค่จๆฏ
@mcp.tool()
async def get_pipeline_history(
name: str,
limit: int = 5,
) -> str:
"""Get execution history for a saved pipeline.
Shows past execution results with diff analysis: which articles are new
compared to the previous run.
Args:
name: Name of the saved pipeline.
limit: Maximum number of history entries to return (default: 5).
Returns:
Execution history with date, article count, new/removed articles, status.
"""ๅๅณๆ ผๅผ๏ผ
๐ Execution History for "weekly_remimazolam" (showing 5 of 12):
| # | Date | Articles | New | Removed | Status |
|---|---------------------|----------|-----|---------|--------|
| 12| 2026-02-15 09:00 | 15 | +3 | -0 | โ
OK |
| 11| 2026-02-08 09:00 | 12 | +1 | -0 | โ
OK |
| 10| 2026-02-01 09:00 | 11 | +2 | -1 | โ
OK |
| 9| 2026-01-25 09:00 | 10 | +0 | -0 | โ
OK |
| 8| 2026-01-18 09:00 | 10 | +4 | -0 | โ
OK |
Latest new articles (run #12):
1. PMID 39876543 - "Remimazolam vs propofol for ICU sedation..." (2026)
2. PMID 39876100 - "Safety profile of remimazolam in critically..." (2026)
3. PMID 39875999 - "Pharmacokinetics of remimazolam in renal..." (2026)
๐ก Full details: fetch_article_details(pmids="39876543,39876100,39875999")
่ก็บ๏ผ
- ๅพ pipeline ๆๅจ scope๏ผworkspace ๆ global๏ผ่ฎๅ
pipeline_runs/{name}/็ฎ้ - ๆฏ็ญๅท่ก่จ้ๅฒๅญ๏ผPMID ๅ่กจ + ๅ 5 ็ฏ metadata๏ผๆจ้ก/ๅนดไปฝ/ๆๅ๏ผ
- Diff ่จ็ฎ๏ผ่ๅไธๆฌกๅท่ก็ PMID ้ๅๅทฎ็ฐ
- ไธๅญๅจๅท่กๆญทๅฒๆๅๅณๆ็คบใๅฐๆชๅท่ก้ใ
@mcp.tool()
async def schedule_pipeline(
name: str,
cron: str = "",
diff_mode: bool = True,
notify: bool = True,
action: str = "set",
) -> str:
"""Schedule a saved pipeline for periodic execution, or list all schedules.
Args:
name: Saved pipeline name. Use "*" with action="list" to list all schedules.
cron: Cron expression (5-field). Empty string with action="set" removes schedule.
Examples: "0 9 * * 1" (Mon 9am), "0 0 1 * *" (monthly), "0 */6 * * *" (6h).
Minimum interval: 1 hour.
diff_mode: When True, only report articles not seen in previous run.
notify: When True, emit MCP resource notification on new results.
action: "set" (default) to create/update/remove schedule,
"list" to list all active schedules,
"status" to show specific pipeline schedule status.
Returns:
Schedule confirmation, list of schedules, or status details.
"""action="set" ๅๅณ๏ผ
โฐ Schedule set for "weekly_remimazolam":
Cron: 0 9 * * 1 (Every Monday at 09:00 UTC)
Next run: 2026-02-17 09:00 UTC
Diff mode: enabled (only new articles)
Notify: enabled
action="list" ๅๅณ๏ผ
๐
Active Schedules (2 total):
| Pipeline | Cron | Next Run | Last Run | Status |
|------------------------|---------------|---------------------|---------------------|---------|
| weekly_remimazolam | 0 9 * * 1 | 2026-02-17 09:00 | 2026-02-10 09:00 | โ
OK |
| crispr_monthly | 0 0 1 * * | 2026-03-01 00:00 | 2026-02-01 00:00 | โ
OK |
๐ก Modify: schedule_pipeline(name="<name>", cron="<new>")
๐ก Remove: schedule_pipeline(name="<name>", cron="")
action="status" ๅๅณ๏ผ
๐ Schedule status for "weekly_remimazolam":
Cron: 0 9 * * 1 (Every Monday at 09:00 UTC)
Enabled: true
Next run: 2026-02-17 09:00 UTC
Last run: 2026-02-10 09:00 UTC
Last status: โ
Success (15 articles, 3 new)
Total runs: 12
Diff mode: enabled
็ดๆ๏ผ
nameๅฟ ้ ๆฏๅทฒๅญๅจ็ saved pipeline- ๆๅฐ cron ้้๏ผ1 ๅฐๆ
- ๅๆๆๅค 5 ๅๆดป่บๆ็จ
- ๆฏๆฌกๆ็จๅท่ก่ถ ๆ๏ผ5 ๅ้
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Agent ไฝฟ็จๆต็จๅ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ ไฝฟ็จ่
ๅ้ก โโโโฌโโโโ ็ฐกๅฎๆฅ่ฉข โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ "ๆๅฐ remimazolam" โ โ
โ โ โผ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ unified_search(query=...) โ โ
โ โ โ โ ๅณๆๅๅณ็ตๆ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโ ่ค้/้่ค้ๆฑ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ "ๆฏ้ฑๆๅฐ remimazolam vs propofol" โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Step 1: ๅปบ็ซ pipeline โ โ
โ โ save_pipeline(name="weekly_remi", config="template: pico\n...") โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ Step 2: ๆธฌ่ฉฆๅท่ก โ โ
โ โ unified_search(pipeline="saved:weekly_remi") โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ Step 3: ่จญๅฎๆ็จ๏ผๅฏ้ธ๏ผ โ โ
โ โ schedule_pipeline(name="weekly_remi", cron="0 9 * * 1") โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ Step 4: ไนๅพไปปไฝๆๅ โ โ
โ โ unified_search(pipeline="saved:weekly_remi") โ ๆๅ้่ท โ โ
โ โ load_pipeline(source="weekly_remi") โ ๆฅ็/็ทจ่ผฏ โ โ
โ โ list_pipelines() โ ๅ่ๆๆ โ โ
โ โ schedule_pipeline(name="*", action="list") โ ๆฅ็ๆ็จ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ ้้ตๅๅ๏ผ โ
โ โข unified_search ๆฏๅฏไธ็ใๆๅฐๅท่กใๅ
ฅๅฃ โ
โ โข Pipeline ๅทฅๅ
ทๅชๅ CRUD + ๆ็จ็ฎก็๏ผไธ็ดๆฅๅท่กๆๅฐ โ
โ โข ๆ็จ่งธ็ผๆ็ฑ PipelineScheduler ๅ
ง้จๅผๅซ PipelineExecutor โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
| ๅฑค | ๆฐๅข/ไฟฎๆน | ๆชๆก่ทฏๅพ | ่ชชๆ |
|---|---|---|---|
| Domain | ๆฐๅข | domain/entities/pipeline.py |
ๆฐๅข PipelineMeta, PipelineRun, ScheduleEntry |
| Application | ๆฐๅข | application/pipeline/store.py |
PipelineStore CRUD + ้ๅฑค scope ่งฃๆ |
| Application | ๆฐๅข | application/pipeline/scheduler.py |
PipelineScheduler tick loop |
| Application | ไฟฎๆน | application/pipeline/executor.py |
ๆฐๅข run_and_store() ๆนๆณ |
| Presentation | ๆฐๅข | presentation/mcp_server/tools/pipeline_tools.py |
6 ๅ MCP ๅทฅๅ ท |
| Presentation | ไฟฎๆน | presentation/mcp_server/tools/__init__.py |
่จปๅ pipeline tools |
| Presentation | ไฟฎๆน | presentation/mcp_server/tool_registry.py |
ๆฐๅข pipeline category |
| Presentation | ไฟฎๆน | presentation/mcp_server/resources.py |
ๆฐๅข Resource templates |
| Presentation | ไฟฎๆน | presentation/mcp_server/server.py |
Lifespan ๆดๅ scheduler |
| Infrastructure | ไฟฎๆน | container.py |
DI ่จปๅ PipelineStore + Scheduler |
| Tests | ๆฐๅข | tests/test_pipeline_store.py |
Store CRUD + scope ๆธฌ่ฉฆ |
| Tests | ๆฐๅข | tests/test_pipeline_scheduler.py |
Scheduler ๆธฌ่ฉฆ |
| Tests | ๆฐๅข | tests/test_pipeline_tools.py |
MCP ๅทฅๅ ทๆดๅๆธฌ่ฉฆ |
| Tests | ๆฐๅข | tests/test_pipeline_history.py |
ๅท่กๆญทๅฒ + diff ๆธฌ่ฉฆ |
pipeline_manage(action="save|list|load|delete", name=..., config=...)
pipeline_schedule(action="set|remove|list|status", name=..., cron=...)ๆชๆก็จๅๅ ๏ผaction ๅๆธ่ฎ Agent ้่ฆ็่งฃๅญๅฝไปค่ช็พฉ๏ผๅขๅ ่ช็ฅ่ฒ ๆใไธ็ฌฆๅ MCP ๅทฅๅ
ทใไธๅๅทฅๅ
ท = ไธๅๅไฝใ็่จญ่จๅฒๅญธใ
unified_search(pipeline="save:my_plan")
unified_search(pipeline="list")
unified_search(pipeline="delete:my_plan")ๆชๆก็จๅๅ ๏ผunified_search ็่ช็พฉๆฏใๅท่กๆๅฐใ๏ผCRUD ๆไฝ่ช็พฉไธ็ฌฆใpipeline ๅๆธ้่ผๅฐ่ด็จ้ๆจก็ณใ
# ็พ่กๆนๅผ๏ผAgent ๅจ unified_search ไธญ inline ๅณๅ
ฅ
# unified_search(pipeline="...")
template: comprehensive
template_params:
query: "remimazolam sedation"
sources: "pubmed,openalex"
limit: 20# ๆ่ญฐ็ๆไน
ๅๆนๅผ
# Step 1: save_pipeline(name="weekly_remi", config="...")
# Step 2: unified_search(pipeline="saved:weekly_remi")
# Step 3: schedule_pipeline(name="weekly_remi", cron="0 9 * * 1")# FastMCP resource template ่ชๆณ
@mcp.resource("pipeline://saved/{name}")
async def read_saved_pipeline(name: str) -> str:
"""MCP clients can read saved pipelines via resources/read."""
store: PipelineStore = ctx.request_context.lifespan_context["pipeline_store"]
config = store.load(name)
return yaml.dump(config.to_dict(), allow_unicode=True)| ่กจ้ๅผ | ๅซ็พฉ |
|---|---|
0 9 * * 1 |
ๆฏ้ฑไธ 09:00 |
0 0 1 * * |
ๆฏๆ 1 ๆฅ 00:00 |
0 */6 * * * |
ๆฏ 6 ๅฐๆ |
30 8 * * 1-5 |
้ฑไธ่ณไบ 08:30 |
0 0 * * 0 |
ๆฏ้ฑๆฅ 00:00 |