Skip to main content
Glama
shogo-hs

guidepost-mcp

by shogo-hs

guidepost-mcp

Ein Server, der Leitbäume (Entscheidungsbäume) über MCP durchlaufen lässt. Wenn der Agent das Label einer Antwort wirft, wird regelbasiert zurückgegeben, was als Nächstes zu prüfen oder zu leiten ist. Am Ende des Baums ist die Führung abgeschlossen.

Selbstschleifen-Agenten sind flexibel, nehmen aber bei derselben Anfrage jedes Mal einen anderen Weg. Bei Vorgängen wie Rückerstattungen oder Identitätsprüfungen, die nicht verfehlt werden dürfen, hält das weder einer Prüfung noch einer Eskalationsentscheidung stand. Die Stellen, an denen Entscheidungen nötig sind, werden als Entscheidungsbaum extern fixiert, und nur die Formulierung wird dem Agenten überlassen.

Nicht nur für den Kundenservice. Da das Vokabular des Entscheidungsbaums nicht domänenabhängig ist, kann es für alle Arbeiten verwendet werden, die Verfahren Schritt für Schritt durchlaufen.

  顧客の発話                      guidepost-mcp (MCP · 8127)
      │                    ┌──────────────────────────┐
  ┌───▼────────┐  values   │ flows/*.yaml ← 起動時に   │
  │  エージェント  ├──────────▶│   メモリ常駐(読むだけ)  │
  │             │◀──────────┤ engine = 純関数で遷移     │
  └────────────┘  next     │ SQLite = runs / steps    │
      │  発話をラベルに        └──────────┬───────────────┘
      │  落とすのはこちら側                │ 読み取り専用
      ▼                              ┌───▼──────────┐
   顧客へ返す                          │ Web UI (SSR)  │ いま樹形図のどこにいるか
                                     └───────────────┘

Die Interpretation von natürlicher Sprache zu Labels liegt beim Agenten. Der Server wechselt nur anhand des empfangenen Labels, daher werden keine zusätzlichen LLM-Aufrufe erzeugt. Gemessen (Raspberry Pi 5) beträgt die Berechnung des Übergangs p95 0,006 ms, mit SQLite p95 0,76 ms und Ende-zu-Ende über HTTP-MCP p95 9,1 ms. Eine Sprachantwortrunde dauert insgesamt etwa 2,5 Sekunden, also 0,4 % selbst mit HTTP. Die Aufschlüsselung und wie das Budget aufgeteilt wurde, finden Sie in docs/research/voice-agent-latency-budget.md.

Schreiben eines Entscheidungsbaums

flows/<flow_id>.yaml ist ein Entscheidungsbaum. Es gibt nur vier Knotentypen.

kind

Rolle

Verzweigung

ask

Eine Bestätigung abfragen und anhand des Antwortlabels verzweigen

Ja

tell

Eine Anleitung mitteilen

Nein

collect

Mehrere unabhängige Punkte in beliebiger Reihenfolge sammeln

Nein

end

Endknoten. Hat ein outcome

Nein

id: payment_failed
title: 支払いが失敗した
entry: n_error_code
max_unmatched: 3          # 聞き直しの上限
max_branch_fanout: 3      # これより枝が多いと畳まない
branch_depth: 2           # 枝を辿って結末を探す深さ
on_unknown: broaden       # 分からないと言われたとき。broaden / escalate
on_stuck: 原因が絞れないため、決済窓口の担当者に引き継ぐ

nodes:
  - id: n_error_code
    kind: ask
    say: 決済画面に出ているエラーコードを確認する     # 逐語原稿ではなく「何を伝えるか」
    accepts:                                      # ラベル → そのラベルに落とす条件
      E01: カードが拒否された
      E02: 残高不足・限度額超過
    next:
      E01: n_card_age
      E02: n_balance
      __other__: n_symptom        # 想定外のラベルの逃がし先(任意)
      __unknown__: n_generic      # 分からないときの逃がし先(任意)

  - id: n_identity
    kind: collect
    say: 本人確認に必要な情報を集める
    on_unknown: escalate          # 重要な手続きなので畳ませない
    slots:
      order_id:
        ask: 注文番号を聞く
        required: true            # 埋まらないと進めない
      phone: 登録の電話番号を聞く   # 短い書き方(任意扱い)
    next: n_verify

  - id: n_resolved
    kind: end
    outcome: resolved
    say: 解消したことを確認し、対応を締める

say ist kein wörtliches Skript, sondern Material dafür, was zu vermitteln ist. Die Formulierung passt der Agent der Situation an. Ein Knoten = eine Bestätigung oder eine Anleitung.

Nach dem Schreiben wird verifiziert. Unerreichbare Knoten oder Labels ohne Ziel funktionieren beim Schreiben noch, sodass sie erst zur Laufzeit auffallen.

uv run guidepost-mcp lint flows/          # CI でも回している
uv run guidepost-mcp show payment_failed --flows flows   # 樹形図を木で表示
uv run guidepost-mcp drafts               # 預かっている草案の一覧
uv run guidepost-mcp approve refund_request   # 草案を検査し直して flows/ へ移す
uv run guidepost-mcp discard refund_request   # 草案を捨てる

Related MCP server: sop-mcp

MCP-Tools

Tool

Rolle

guide_flows(category)

Ohne Argumente: Kategorienliste und Anzahl, mit category die Liste der Flows darin

guide_start(flow_id, subject, agent)

Startet einen Lauf. run_id + erster Knoten + Index

guide_answer(run_id, choice, values, utterance)

Wirft eine Antwort. Gibt einen der 5 unten genannten Zustände zurück

guide_state(run_id)

Aktuelle Position, Pfad, gesammelte Werte. Für Wiederaufnahme und Übergabe

guide_revise(run_id, to_node, clear)

Zurückgehen. Korrigierte Werte verwerfen

guide_close(run_id, outcome, reason)

Abschließen, ohne bis zum Ende zu gehen

guide_draft(yaml)

Nimmt den Entscheidungsbaum als Entwurf entgegen. Gibt Lint-Hinweise zurück. Erscheint nicht in der Bearbeitung

Die Auswahl des Einstiegs erfolgt in zwei Stufen, die Registrierung des Entscheidungsbaums bleibt beim Entwurf

Da bei vielen Entscheidungsbäumen nicht mehr alle zurückgegeben werden können, gibt guide_flows() zuerst die Kategorien (definiert in flows/categories.yaml) und die jeweilige Anzahl zurück, und mit guide_flows(category=...) die Liste der darin enthaltenen Flows. Passt keiner der Kategorien, wird nicht zwanghaft zugeordnet, sondern ohne Entscheidungsbaum bearbeitet.

guide_draft nimmt den Entscheidungsbaum nur entgegen; er erscheint weder in flows/ noch in guide_flows(). Erst wenn eine Person ihn mit guidepost-mcp approve freigibt, wird er zur verbindlichen Version. Die Erstellung des Entscheidungsbaums ist eine Aufgabe außerhalb dieses Servers und ist auch nicht für die Erstellung während der Bearbeitung vorgesehen (Begründung siehe docs/adr/0011-draft-intake.md).

Antworten werden gesammelt und automatisch so weit vorgerückt, wie sie ausgefüllt sind

Wenn der Kunde auf einmal sagt: „E01 ist aufgetreten, und die Karte ist von vor drei Jahren“, möchte man nicht jede Frage einzeln wiederholen, obwohl die Antworten bereits vorliegen. Wenn man alles Bekannte in values übergibt, wird so weit vorgerückt, wie ausgefüllt ist, und am ersten nicht ausgefüllten Knoten angehalten.

  収集済み: {n_error_code: E01, n_card_age: over_1y}

  n_error_code ──E01──▶ n_card_age ──over_1y──▶ n_expiry_check ──?──▶ …
   ✓ 聞かずに通過        ✓ 聞かずに通過           ▲ ここで止まる

Übersprungene Knoten werden als skipped zurückgegeben. Damit der Agent die IDs der späteren Knoten kennt, übergibt guide_start einmalig einen Index (eine Zeile pro Knoten/Slot, was abgefragt wird).

Die beliebige Reihenfolge von collect funktioniert genauso. Egal in welcher Reihenfolge ausgefüllt wird, sobald alles vollständig ist, wird passiert.

Nicht bei „Weiß nicht“ anhalten

Es kommt häufig vor, dass die anfragende Person die Antwort nicht hat. Auch hartnäckiges Nachfragen bringt sie nicht hervor.

   guide_answer
        ├─ ラベルが accepts にある ──────────▶ advanced / completed
        ├─ accepts に無い ──────────────────▶ unmatched(聞き直す)
        │                                       │ max_unmatched 回で下へ
        └─ choice="__unknown__" ──────────┐   │
                                          ▼   ▼
                               next.__unknown__ があるか
                                  ├─ ある ─▶ advanced(逃がし先へ)
                                  └─ 無い ─▶ on_unknown は
                                              ├─ escalate ─▶ stalled(有人へ)
                                              └─ broaden ──▶ 枝を畳めるか
                                                   ├─ できる ─▶ branched
                                                   └─ 無理 ───▶ stalled

branched ist ein Zustand, in dem ohne die Zweige festzulegen, die Ergebnisse jedes Zweigs nebeneinander angeleitet werden. Es wird Material zurückgegeben, um „Bei E01 wenden Sie sich an die Kreditkartenfirma, bei E02 prüfen Sie den Kontostand“ zu formulieren. Wenn es einen Knoten gibt, in dem alle Zweige zusammenlaufen, wird er in common aufgenommen, sodass man „In jedem Fall am Ende ...“ zusammenfassen kann.

Bei Verfahren wie Rückerstattung oder Identitätsprüfung, bei denen eine vage Anleitung Schaden anrichten kann, schreibt man on_unknown: escalate, um sie nicht zusammenfalten zu lassen. Nicht faltbare Knoten werden von lint namentlich genannt, sodass man nur dort nachbessern muss.

Start

uv sync
uv run uvicorn guidepost_mcp.web:app --host 127.0.0.1 --port 8127
uv run python scripts/mcp_smoke.py            # 実プロトコルで 1 周辿る
uv run python scripts/mcp_smoke.py --parallel # 2 本の run を交互に進める

Die Weboberfläche ist unter http://127.0.0.1:8127/ erreichbar. Laufende Runs werden aufgelistet, und unter /r/<run_id> werden auf dem Entscheidungsbaum die aktuelle Position, der zurückgelegte Pfad, die durch Vorausschau übersprungenen Knoten und die zusammengefalteten Knoten farblich markiert.

Von der Agentenseite wird über HTTP als MCP verbunden.

from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient(
    {
        "guidepost-mcp": {"url": "http://127.0.0.1:8127/mcp/", "transport": "streamable_http"},
    }
)
tools = await client.get_tools()

Version

Die maßgebliche Version ist die version in pyproject.toml und folgt SemVer (während 0.x können in Minor-Versionen breaking changes auftreten). Die Änderungshistorie finden Sie in CHANGELOG.md.

Was als breaking change gilt, ist in docs/adr/0009-versioning.md definiert. Der Kern sind das YAML-Schema des Entscheidungsbaums und der Vertrag der MCP-Tools (Toolnamen, Argumente, Schlüssel der Rückgabewerte, die fünf Werte von status); der Wortlaut der Anweisungen in next ist nicht enthalten.

Die version in flows/*.yaml ist die Version des Bearbeitungsablaufs und unabhängig von der Version dieser Bibliothek.

Hintergrund des Designs

Warum die Labelinterpretation beim Agenten liegt, warum bei „Weiß nicht“ Zweige zusammengefaltet werden und warum die Weboberfläche schreibgeschützt ist, ist mit Begründung in docs/adr/ dokumentiert (Liste siehe docs/adr/README.md). Die Recherchen, die als Grundlage für Entscheidungen dienten, finden Sie in docs/research/.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides browser automation capabilities using Playwright's accessibility tree, enabling LLMs to interact with web pages through structured data without screenshots or vision models. It's designed for specialized agentic loops that benefit from persistent state and iterative reasoning over page structure.
    Apache 2.0
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI agents to execute multi-step Standard Operating Procedures step by step, with enforcement of completion at each step, making LLM behavior predictable and auditable.
    5
    3
    Apache 2.0