mcpField
P
plexus

plexus

リモート
github.com/pcas-ioメモリ・ナレッジ126 週間前に更新
リポジトリ

plexus について

plexusはAIエージェント向けのセルフホスト可能なナレッジグラフです。情報を型付きエンティティと期間付きの関係として保存し、人はダッシュボードで閲覧し、エージェントはMCP経由で編集できます。

主な機能

  1. 01型付きエンティティと関係
  2. 02MCP経由のグラフ書き込み
  3. 03全文検索と関係の探索
  4. 04期間付き関係と履歴検索
  5. 05ユーザー、トークン、権限範囲の管理
  6. 06グラフ検査とスキル読み込み

活用シーン

  1. 01セッションをまたぐエージェント記憶の保存
  2. 02異なるエージェント間の作業引き継ぎ
  3. 03意思決定と根拠の追跡
  4. 04研究資料を関連知識として整理

MCP 対応

plexusはMCP経由で15のツールを公開し、エージェントによるエンティティの検索、読み取り、作成、更新、関係管理を可能にします。アクセス範囲はトークン権限で制御されます。

設定

リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。

エンドポイントhttps://{plexus_host}/mcp
インストールコマンドをコピー
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。

ツール

詳細

plexus(_graph)

AI エージェント向けの型付きオンプレミスナレッジグラフです。人間は読み取り専用、エージェントは書き込み専用で、Model Context Protocol を介してやり取りします。

pcas.io の製品ラインの一部です。

ライセンス Node CI

plexus のグラフビュー — Apollo-program のデモグラフ


要約

ナレッジ管理は20年もの間、文書中心でした。フォルダー、メモ、Wiki、エディターをめぐる論争などです。plexus はその考え方を逆転させます。

plexus では、すべての情報が concept、decision、fact、project、task、document、skill、…といった型付きエンティティであり、その間は厳密に定義された、時間的に有効なエッジでつながれています。自由形式のテキストも、忘れられたタグも、「あとで整理しよう」もありません。書き込みとリンク作成は、エージェントからのMCP 経由でのみ行われます。ダッシュボードにはグラフが表示されますが、編集ボタンはありません。

これにより、次の3つが実現します。

  1. エージェントはあなたに尋ねる必要がありません。種類と関係性の規律を厳守しながら、自ら構築します。
  2. lint、置き換え、来歴の記録がワークフローに組み込まれているため、グラフが古くなりません。
  3. グラフ全体ではなく、ひとつのアイデアだけを共有できます。パスキーで保護された使い捨てリンクは、最初のクリックで無効になります。

自分のハードウェア上で Docker Compose を使って実行できます。Apache-2.0 ライセンスです。

LLM 向け Wiki と考えてください

従来の Wiki は人間の読者向けに作られています。自由形式の文章、人手で整理されたリンク、手入れをやめた途端に劣化するページツリーが特徴です。LLM をその前に置くと、LLM が最初にするのは要約を頼むことです。

plexus は対象読者を逆転させます。LLM が読み手であり書き手です。各ページは段落ではなく型付きレコードであり、各リンクはハイパーリンクではなく名前付きの関係であり、各編集は Markdown の差分ではなく、アトミックな MCP 呼び出しです。UI にはエージェントが見るものと同じグラフが表示されますが、その整理を行うのはエージェントです。

これは、Andrej Karpathy が"The LLM wiki" gist(2026年4月、5000以上のスター)で描いたパターンと同じです。クエリのたびに生の文書に対してステートレスな RAG を行うのではなく、LLM が知識を構造化され相互参照された Wiki に一度コンパイルし、最新の状態に保ちます。plexus はこのパターンをインフラストラクチャ上で実現します。

Karpathy の LLM Wikiplexus
生のソース(変更なし)エージェントが web_fetch、ファイル I/O、…で読み取る外部文書
Wiki(型付き Markdown、整理済み)エージェントが MCP 経由で書き込む、グラフ内の型付きエンティティと名前付きエッジ
スキーマ(CLAUDE.md/AGENTS.md 内の規約)MCP の境界で適用される list_kinds と list_relations のレジストリ
取り込み → コンパイル → 振り返り → クエリ → lintsearch_entities → save_entity/update_entity → link_entities → get_related → lint_graph

Karpathy はこの gist を、ベンダーに依存しないプロンプトパターンである「アイデアファイル」と呼んでいます。plexus はそれを本番環境向けのバックエンドで実現します。型付きストレージ、楽観的ロック、時間的エッジ、マルチユーザー認証、監査ログを備えています。

これにより、次のことが可能になります。

  • セッションをまたいだ単一の信頼できる情報源。 エージェントは会話のたびに開始時にコンテキストを読み込みます。あなたが自分のことを説明し直す必要はありません。
  • エージェントをまたいだ単一の信頼できる情報源。 Claude web、Claude Code、Claude Desktop、Le Chat、Gemini など、どのクライアントが接続しても、同じグラフを使います。
  • 人間をまたいだ単一の信頼できる情報源。 すぐに使えるマルチユーザー対応、ユーザーごとにスコープを設定したトークン、共有の project コンテナ、監査ログを備えています。

目次

理解する

  • ユースケース — plexus が想定する具体的なシナリオを7つ紹介します

実行する

エージェントを接続する

リファレンス

運用する


ユースケース

エージェントの永続メモリ。 会話のたびに context_load を実行することで、アシスタントは、すでに決定した ADR、稼働中のプロジェクト、昨日完了したタスクを記憶できます。「ご存じかもしれませんが……」というプロンプトエンジニアリングは不要です。

エージェント間の引き継ぎ。 Claude Code が機能の実装を終え、引き継ぎ用のファクトを書き込みます。翌朝、Claude web は同じコンテキストを引き継ぎ、どのコミットがリリース済みで何が未完了かを把握します。説明し直す必要はありません。

小規模なグループのチームメモリ。 インスタンスを1つ立ち上げ、メンバーごとにスコープを限定した個人用トークンを発行し、project コンテナを共有します。全員が閲覧できるダッシュボードを用意し、エージェントが全員のために書き込みます。Notion も wiki も Slack の過去ログの掘り起こしも不要です。

意思決定の経緯をたどる。 すべての ADR は、supersedes/derived_from エッジを持つ一級の decision エンティティです。1年後には、F-1 エンジンを採用し続けた 理由、Apollo 13 の事後検証で 何が引き起こされたか、どの設計がどれに取って代わったかをたどれます。get_related(as_of: "2026-01-01") を使って、過去の時点を指定したクエリを実行できます。

インシデントから修正までのループ。 本番環境のインシデントを severity: high を持つ fact にします。緩和策を記した ADR から、そのファクトへ triggered_by でリンクします。ランブックから ADR へ documents でリンクします。6か月後に入社した人も、Slack のスレッドを12個読む代わりに、グラフをたどるだけで状況を把握できます。

文献/調査の要約。 エージェントが論文を取り込み、ソースの note を作成し、concept と fact のエンティティを抽出して、mentions エッジを付与します。トピックでグラフを検索でき、すべての主張について、derived_from で元のノートまでたどれます。

エージェントが実際に使う、プライベートな「第二の脳」。 inbox_item で情報を取り込み、週次レビューで task/concept/fact に昇格させ、supersedes でアーカイブします。フォームを操作しなくても、メモを構造化できます。


クイックスタート

# 1. Clone
git clone https://github.com/pcas-io/plexus.git
cd plexus

# 2. Configure — generates .env from the template with fresh 32-byte
#    secrets for all four required fields:
#      PLEXUS_ADMIN_TOKEN      ← you'll use this once, to bootstrap
#      PLEXUS_OAUTH_SECRET     ← OAuth state signing
#      PLEXUS_COOKIE_SECRET    ← dashboard session cookie signing
#      PLEXUS_SURREAL_PASS     ← SurrealDB root password
scripts/bootstrap_env.sh

# 3. Start
docker compose up -d

# 4. Open the dashboard
open http://localhost:8787

初回ログインに必要な管理者トークンは .env にあります。

grep '^PLEXUS_ADMIN_TOKEN=' .env

この16進数の値をコピーしてください。次のセクションで、一度限りの初期設定の手順を説明します。

設定の詳細は.env.exampleを参照してください。Coolify、通常の Docker、Kubernetes へのデプロイに関する注意事項は以下に記載しています。


管理者ユーザーを作成する(初回起動時)

plexus は、ユーザーがいない空のデータベースで起動します。最初の管理者アカウントを作成するために、PLEXUS_ADMIN_TOKEN を一度だけ使用します。その後、トークンは使用されなくなります。

1. 管理者トークンを確認する

クイックスタートで実行した scripts/bootstrap_env.sh スクリプトによって、トークンは .env に書き込まれています。次のコマンドで確認してください。

grep '^PLEXUS_ADMIN_TOKEN=' .env
# PLEXUS_ADMIN_TOKEN=<64-char-hex>

= の後ろにある16進数の値をコピーしてください。ダッシュボードに一度だけ貼り付けます。初期設定後、plexus は MCP と通常のログインの両方で管理者トークンを拒否します。

2. フローを確認する
#実行することplexus の動作
1http://localhost:8787 を開くログインフォームが表示されます
2管理者トークンを貼り付け、続行をクリックします「管理者トークンがあり、管理者ユーザーが存在しない」ことを認識し、/bootstrap にリダイレクトします
3管理者名(英字、数字、_、-、.)を入力し、管理者を作成します管理者ユーザーを作成し、新しい個人用トークン pt_… を一度だけ表示します。コピーしてください。
4ログインに進むをクリックします/auth/login に戻り、pending-auth cookie が設定されます
5pt_ トークンを貼り付けますパスキー登録画面が表示されます
6今すぐパスキーを登録をクリックし、Touch ID/Face ID/Windows Hello/YubiKey で確認しますパスキーを保存した後、pt_ トークンをローテーションし、新しいトークンを一度だけ表示します。パスワードマネージャーの登録内容を上書きしてください。
7新しいトークンでサインインをクリックし、新しいトークンを貼り付けて、パスキーをタップします/home が表示されます

以降、管理者トークンは管理プレーンのエンドポイント(バックアップ、リセット)でのみ使用します。MCP では拒否され、通常のダッシュボードへのログインにも使えません。

トークンをローテーションする理由

初期トークン(管理者トークンと最初の pt_)は、一度限りの招待コードです。メール、チャット、画面共有などで傍受された場合でも、サインインした時点でローテーションにより、その情報は無価値になります。/users から作成する追加ユーザーにも、同じ管理者からユーザーへの引き継ぎと初回登録時のローテーションが適用され、同様に保護されます。

次に進むには

ダッシュボード内の /help ページに、MCP の統合、トークンのスコープ設定、パスキーの管理、共有リンク、グラフビューなど、その他のすべての情報が掲載されています。

plexus ヘルプページ — 初回セットアップのドキュメント


エージェントを接続する

plexus は POST /mcp 経由で Model Context Protocol を使用します。MCP 対応クライアントであれば、どれでも接続できます。

Claude.ai(ウェブ)

設定 → 統合 → カスタム統合を追加 → https://<your-plexus-host>/mcp。plexus が OAuth を自動で処理するため、同意画面は一度だけ表示されます。

Claude Code(CLI)
claude mcp add plexus --transport http \
  --header "Authorization: Bearer pt_YOUR_TOKEN" \
  https://<your-plexus-host>/mcp
Claude Desktop

~/Library/Application Support/Claude/claude_desktop_config.json に記述します。

{
  "mcpServers": {
    "plexus": {
      "type": "http",
      "url": "https://<your-plexus-host>/mcp",
      "headers": { "Authorization": "Bearer pt_YOUR_TOKEN" }
    }
  }
}

MCP ツール

plexus は 15 個のツールを公開します。

ツール目的スコープ
save_entity新しいエンティティを作成書き込み
get_entityID で単一のエンティティを取得読み取り
list_entitiesフィルター(kind、context、status)を指定して一覧表示読み取り
search_entities必要に応じてハイライト表示できる、BM25 ランキングによる全文検索読み取り
update_entity楽観的ロック(expected_version)を使用して更新書き込み
archive_entity論理削除(status → archived)書き込み
link_entities2 つのエンティティ間に時間的なエッジを作成書き込み
unlink_entityエッジを無効化(valid_to = now)書き込み
get_related方向と時点フィルターを指定してエッジをたどる読み取り
list_kindskind レジストリを照会読み取り
list_relationsrelation レジストリを照会読み取り
context_loadセッションのウォームアップ — 最新のエンティティ 20 件とレジストリ読み取り
lint_graph孤立したエンティティとタイトルの重複を確認読み取り
list_skillsすべてのスキルの簡潔なインデックス読み取り
load_skill名前、トリガーフレーズ、または BM25 フォールバックを指定してスキルを 1 つ読み込む読み取り

すべてのツールはハンドラーの境界でスコープを適用します。requireWrite()、checkContext()、checkKind()、rejectSecret() が使用されます。kind=secret は MCP 経由では到達できず、すべての読み取りレスポンスから除外されます。

plexus ダッシュボードのホーム — プロジェクト、アクティビティフィード、コンテキストフィルター

エージェントのシステムプロンプトテンプレート

このブロックを、MCP 対応エージェント(Claude のウェブ版カスタム統合、Claude Code CLI、Claude Desktop、Le Chat、カスタム SDK ハーネス)に貼り付けてください。意図的に簡潔にしており、どの行にも役割があります。モデルに依存せず、MCP ツール呼び出しに対応するあらゆる LLM で動作します。

# Plexus is your long-term memory

You have access to **plexus**, a persistent on-prem knowledge graph,
over MCP. Treat it as authoritative memory across sessions. Never
re-derive what the graph already knows; never let the graph drift.

## Every session

1. **Start:** call `context_load` once. Read the 20 most recent
   entities and the kinds/relations registries. No other lookups
   until you know what's already there.
2. **Before writing:** call `search_entities` (BM25, 2–4 keywords) to
   avoid duplicates. If you find a close match, `update_entity` with
   `expected_version`; do not create a new one.
3. **Writing new knowledge:** `save_entity` with the right `kind`.
   Pick deliberately — kind drives how other agents will query it.
   - `concept` — pattern, idea, domain term
   - `decision` — an ADR (see "ADR discipline" below)
   - `fact` — atomic statement, incident, milestone
   - `project` — container that other entities are `part_of`
   - `task` — actionable item (`attributes.is_milestone: true` for
     milestones)
   - `document` — runbook, post-mortem, design doc
   - `note` — provenance anchor for an external source (URL, paper)
   - `config` / `secret` — URLs, env vars, credentials. Secrets are
     MCP-unreadable by design; do not attempt to read them.
   - `inbox_item` — GTD quick capture to triage later
4. **Linking:** pick a named relation, not `relates_to`:
   `part_of`, `depends_on`, `blocks`, `supersedes`, `documents`,
   `derived_from`, `triggered_by`, `implements`, `produces`,
   `consumes`, `mentions`, `owned_by`, `has_version`, `variant_of`.
5. **Traversal:** prefer one `get_related` over multiple
   `get_entity` calls. Use `as_of` for point-in-time queries.
6. **Graph hygiene:** at the end of a substantial session, call
   `lint_graph` and fix orphans and duplicate titles.

## ADR discipline

Every `kind=decision` MUST carry at least one edge —
`derived_from`, `triggered_by`, `supersedes`, or `part_of`. A
free-floating decision is a bug; the next agent won't find it.

## Session handoff

At the end of every non-trivial session, save a handoff-fact:

- `kind: "fact"`
- `attributes.session_type: "handoff"`
- `attributes.session_date: "<ISO date>"`
- `attributes.session_id: "<short slug>"`
- `attributes.agent_id: "<model id>"`
- `attributes.git_branch: "<branch>"` if applicable
- `part_of: "<project-entity-id>"` (required for handoffs, not
  optional — server enforces this)

Title format: `"<Project> Handoff <Date> — <Core topic>"`.

## Optimistic locking

Every `update_entity` requires `expected_version`. On
`version_conflict`, re-fetch with `get_entity` and retry; never
blindly overwrite.

## Versioning, not deletion

Never delete by hand. Supersede: create the new entity, link
`supersedes <old>` (1:1), then `archive_entity` the old one.

## Memory routing — when plexus, when local scratchpad

**Goes in plexus (the default):**
- Anything team-, project-, architecture- or decision-relevant.
- Incidents, milestones, post-mortems, ADRs.
- Anything an agent in another session will want to know.

**Keep in a local scratchpad instead:**
- Per-session ephemera (current file paths, interim REPL output).
- Host-local gotchas with no team value (a specific IDE's template
  literal trap, one machine's firewall oddity).

**When in doubt, plexus wins.** You can always archive a noisy
entity; you cannot recover one you never wrote.

## Trigger phrases

If the user says any of these, load the matching skill via
`load_skill` before acting:

| Phrase | Skill |
|---|---|
| "ADR" / "decision" | entscheidung / adr |
| "post-mortem" | post-mortem |
| "pre-mortem" | pre-mortem |
| "5 whys" / "root cause" | 5-whys |
| "runbook" | runbook |
| "STRIDE" / "threat model" | stride |
| "weekly review" | weekly-review |
| "meeting notes" | meeting-protokoll |

Skills are `kind=skill` entities in the graph with a trigger-phrase
attribute. `list_skills` gives you the index; `load_skill` returns
the full markdown body.

エージェントの役割に応じて絞り込んでください。 何でもできるトークンを1つ使うのではなく、範囲を限定した個別のトークンを発行します。

  • Writer エージェント(Claude Code CLI):permission=write、すべてのコンテキスト、すべての種類。
  • Reader ウィジェット(ダッシュボード、監視ジョブなど):permission=read、単一の contexts: [dev]、シークレットなし。
  • ドメイン特化型エージェント(例:調査内容の要約エージェント):permission=write、kinds: [note, concept, fact] のみ。

ダッシュボードの /tokens ページでは、これらのトークンを2回のクリックで生成できます。


例:ソースの取り込み

エージェントが実際に plexus に対して実行するときの、Karpathy 方式の取り込みループを紹介します。ユーザーが記事を渡すと、エージェントは plexus ツールだけを使って LLM wiki パターンの5つのフェーズを進め、1分足らずでグラフを充実させます。

ユーザー:「これを取り込んでください:https://example.com/engineering/feature-flags-at-scale」

1 — 分類と状況確認。 エージェントはソースを取得して article に分類し、重複を作らないよう既存の内容を確認します。

search_entities({ query: "feature flags progressive rollout", limit: 10 })
// → 2 concepts, 1 ADR, 1 fact in the dev context

2 — 抽出。 記事の場合、エージェントは重要な主張、新たな事実、決定事項、既存のエンティティと矛盾するものを抽出します。

3 — 型付きエンティティの書き込み。 意図ごとに種類を分け、重複を作成する代わりに既存のエンティティを更新します。

// New concept — the specific rollout pattern the article introduces
save_entity({
  kind: "concept", context: "dev",
  title: "Shadow-flag pre-rollout (Acme pattern)",
  body: "# Shadow flag pre-rollout\n..."
})
// → entities:ab12...

// New atomic fact with provenance-grade attributes
save_entity({
  kind: "fact", context: "dev",
  title: "Acme reduced rollout-related incidents by 62% after adopting shadow flags",
  attributes: { severity: "info", category: "benchmark", source_year: 2026 }
})
// → entities:cd34...

// Existing concept gets enriched, not duplicated
update_entity({
  id: "entities:prev-concept-id",
  expected_version: 3,
  body: body + "\n\n## Update 2026-04-17\n..."
})

4 — 出典の関連付け。 取り込みごとに、出典を示すアンカーとして note をちょうど1つ作成し、抽出した各エンティティから derived_from でそのノートにリンクします。

save_entity({
  kind: "note", context: "dev",
  title: "Source: Acme — Feature flags at scale",
  body: "## Metadata\nURL, date, author.\n\n## Summary\n2–3 sentences.\n\n## Derived entities\n- Shadow-flag pre-rollout (Acme pattern)\n- Acme reduced rollout-related incidents by 62% ..."
})
// → entities:ef56...

link_entities({ from_id: "entities:ab12...", to_id: "entities:ef56...", relation: "derived_from" })
link_entities({ from_id: "entities:cd34...", to_id: "entities:ef56...", relation: "derived_from" })
link_entities({ from_id: "entities:prev-concept-id", to_id: "entities:ef56...", relation: "mentions" })

5 — 2つの出力。 ユーザーへの返答とグラフの更新の両方を行います。後で済ませる作業にせず、同じターン内で完了します。

エージェントの返答:

Acme のフィーチャーフラグに関する記事を取り込みました。新しいエンティティが2件、既存のコンセプトが1件拡充され、新しいソースノート(entities:ef56…)への derived_from/mentions エッジが3件追加されました。シャドーフラグの事前ロールアウトは既存のロールアウト ADR と矛盾せず、相互に補完しています。

6か月後、誰かがエージェントに「どうしてシャドーフラグを採用したんだっけ?」と尋ねます。エージェントは search_entities({ query: "shadow flag rollout" }) を実行し、該当項目に対して get_related を使ってソースノートと、それを引用した ADR を取得し、生の HTML を読み直す代わりに、整理済みのグラフに基づいて回答します。要約し直す必要はありません。「ご存じかもしれませんが」と前置きする必要もありません。wiki に答えがすでにあります。


グラフが把握する内容

種類
種類意味
concept抽象的な知識、パターン
decisionアーキテクチャ決定記録
fact個別の記述、インシデント、マイルストーン
projectコンテナー
task作業項目(マイルストーンの場合は属性 is_milestone を使用)
document設計書、運用手順書、事後分析、プロトコル
noteソース、出典を示すアンカー
template再利用可能なパターン
configURL、環境変数
secret認証情報 — MCP からはアクセス不可。ダッシュボードのみで、追加認証としてパスキーが必要
inbox_itemGTD のクイックキャプチャ
userユーザー参照
tagカテゴリー分類
skillトリガーフレーズを含む Markdown スキル。load_skill で読み込み
関係

contains / part_of · relates_to · depends_on / blocks · supersedes / superseded_by · documents / documented_by · implements · produces / produced_by · consumes · mentions · derived_from · triggered_by · has_version · variant_of · executed_by · owned_by

各エッジには valid_from、valid_to、confidence、source(manual / llm-inferred / computed / imported)が含まれます。unlink_entity は valid_to = now を設定しますが、エッジは履歴として残ります。get_related(as_of: …) を使うと、過去の任意の時点におけるグラフの状態を取得できます。

plexus エンティティ詳細 — Markdown 本文、属性、型付き関係を含む ADR


認証

人間向け:パスキー

ダッシュボードはパスキー専用です。初回ログイン時にトークンとパスキーを使用すると、トークンは自動的にローテーションされます。初期トークンは一度限りの招待コードで、登録が完了すると無効になります。ユーザーごとに最大3つのパスキーを登録できます(ノートPC、スマートフォン、ハードウェアキー)。最後の1つは削除できません。

エージェント向け:パーソナルトークンとOAuth

パーソナルトークン(pt_…)は、ダッシュボードから発行する、権限範囲を明示したファーストパーティのセルフサービス型トークンです。権限範囲は permission × contexts × kinds × expires_in_days で指定します。claude code、CLI による自動化、または長期間有効な認証情報を信頼して渡せる MCP クライアントで使用します。

OAuth 2.1 トークン(ot_…)は、サードパーティの MCP クライアント向けです。plexus は完全な認可サーバーを提供します。

  • RFC 8414(認可サーバーメタデータ)および RFC 9728(保護されたリソースのメタデータ)によるディスカバリー。
  • 動的クライアント登録(RFC 7591)。
  • S256 による PKCE が必須です。plain はスキーマとコードで拒否されます。
  • 認可エンドポイントとトークンエンドポイントの両方で、リソースインジケーター(RFC 8707)に対応します。
  • リダイレクト URI は定数時間で完全一致比較されます。
  • リフレッシュ時にトークンをローテーションします。新しいトークンが発行された時点で古いリフレッシュトークンが失効し、侵害を検知できるようになります。

管理者トークンは /mcp で厳格な 403 エラーとして拒否されます。「とにかく管理者トークンを使う」という抜け道はありません。


共有

グラフ全体を渡さずに、単一のエンティティを読み取り専用の一度限りのリンクとして共有できます。

  1. ダッシュボードでエンティティを開きます。共有をクリックします。
  2. 目的に紐付けられたステップアップのパスキープロンプト(stepup:share:<userId>)が表示されます。共有チャレンジをリセットチャレンジとして再利用することはできません。
  3. plexus は32バイトの st_ トークンを発行します。保存されるのは SHA-256 ハッシュのみです。URL は一度だけ表示されます。
  4. 受信者は GET /share/:token を開きます。ログインは不要で、レート制限が適用され、読み取り専用です。
  5. 2回目の呼び出しでは 410 Gone が返されます。いつでも無効化できます。

共有 URL は1つで、形式は3種類です。

URLContent-Type用途
/share/:tokentext/htmlブラウザー
/share/:token?rawtext/markdownLLM または glow にパイプで渡す
/share/:token?raw=jsonapplication/jsonjq に対応した自動化

kind=secret のエンティティは共有できません。このチェックはトークン作成前に行われます。


検索

search_entities は、カスタムアナライザー(plexus_text)を備えた SurrealDB の BM25 インデックスで実行されます。

  • blank と class のトークナイザー、lowercase と ascii のフィルター(Unicode から ASCII への変換により、ü は u に、ß は ss に一致します)。
  • 2つのインデックス:entities_title_search と entities_body_search。
  • ランキング:search::score(0) * 2 + search::score(1)。タイトルの重みは2倍です。
  • 一致箇所は、オプションで <mark> タグで囲んで強調表示できます。
  • 応答サイズを制限するには、オプションの body_preview_chars(0~2000)を指定します。広範なクエリでは指定を推奨します。

plexus のエンティティブラウザー — 一致箇所を強調表示する BM25 検索


アーキテクチャ

┌──────────────────────────────────────────────┐
│  agents (Claude web / CLI / Desktop / ...)   │
└───────────────┬──────────────────────────────┘
                │ MCP over HTTPS + Bearer token
                ▼
┌──────────────────────────────────────────────┐
│  plexus worker (Hono + TypeScript, Node 22)  │
│  ├─ POST /mcp      — MCP tool dispatcher     │
│  ├─ /oauth/*       — OAuth 2.1 AS + PRM      │
│  ├─ /auth/*        — WebAuthn passkey flow   │
│  ├─ /share/:token  — one-shot read links     │
│  ├─ /dashboard     — read-only human UI      │
│  └─ /admin/backup  — portable JSON export    │
└───────────────┬──────────────────────────────┘
                │ SurrealQL
                ▼
┌──────────────────────────────────────────────┐
│  SurrealDB v2 (RocksDB, persistent volume)   │
│  entities · edges · registries · activity    │
└──────────────────────────────────────────────┘
ソースの構成
src/
├── index.ts              # HTTP bootstrap, CSP, security headers
├── auth/                 # passkeys, sessions, CORS allow-list
├── db/                   # SurrealDB client, repositories, util
├── mcp/                  # MCP tool handlers, skill tools, dispatcher
├── routes/               # dashboard, oauth, shares, admin, well-known
├── ui/                   # layout, styles, pages, markdown renderer
├── backup.ts             # portable JSON export
└── version.ts            # build-embedded version string
migrations/               # SurrealDB schema in migration order
scripts/                  # one-off admin / ops helpers

ファイルサイズの目安となる上限は約700行です。それを超えて大きくなったファイルは、責務ごとに分割されます。


設定

すべての設定は環境変数で行います。デフォルト値を含む一覧は.env.exampleを参照してください。

変数必須説明
PLEXUS_ADMIN_TOKEN✓初期管理者トークンです。本番環境ではシークレットマネージャーを使用してください。
PLEXUS_OAUTH_SECRET✓認可コードと OAuth の state に署名します。
PLEXUS_COOKIE_SECRET✓ダッシュボードのセッションクッキーに署名します。
PLEXUS_SURREAL_URL✓SurrealDB のエンドポイントです。Compose ネットワーク内ではデフォルトで http://surrealdb:8000 になります。
PLEXUS_SURREAL_USER✓SurrealDB の root ユーザーです。
PLEXUS_SURREAL_PASS✓SurrealDB の root パスワードです。
PLEXUS_SURREAL_NS名前空間です(デフォルトは plexus)。
PLEXUS_SURREAL_DBデータベースです(デフォルトは main)。
PLEXUS_RP_IDWebAuthn の証明書利用者 ID です。ホスト名と一致している必要があります。デフォルトは localhost です。
PLEXUS_RP_NAMEパスキーのプロンプトに表示される名前です。
PLEXUS_BASE_URLOAuth メタデータで使用する正規のベース URL です。
PLEXUS_PORTHTTP の待ち受けポートです(デフォルトは 8787)。
PLEXUS_LOG_LEVELdebug / info / warn / error です。

十分なエントロピーを持つシークレットを生成します:

openssl rand -hex 32

セルフホスティング

Docker Compose(推奨)

同梱の docker-compose.yml は、plexus ワーカーと SurrealDB をまとめて起動します。ワーカーは 127.0.0.1:8787 のみで公開されるため、パブリック DNS レコードの入力ミスによってダッシュボードが誤って公開されることはありません。自分のマシン以外からアクセスする場合は、前段にリバースプロキシを配置してください。

docker compose up -d
docker compose logs -f plexus

Coolify ホストでは、Coolify オーバーレイを重ねて、外部の coolify Traefik ネットワークに接続し、ルーティングラベルを固定します:

docker compose -f docker-compose.yml -f docker-compose.coolify.yml up -d
リバースプロキシの背後に配置する場合

plexus の本番環境では TLS が必要です。ダッシュボードは Secure クッキーを設定するため、localhost 以外でプレーン HTTP を使用すると正常に動作しません。Caddy、Traefik、nginx などのプロキシを使用してください。最小限の Caddy 設定は Caddyfile にあります。

CSP ヘッダーでは、D3 用に cdn.jsdelivr.net を(SRI ハッシュとともに)許可しています。別の CDN 経由でダッシュボードを配信する場合は、src/index.ts の CSP を適宜更新してください。

バックアップ
curl -H "Authorization: Bearer $PLEXUS_ADMIN_TOKEN" \
  https://<your-plexus-host>/admin/backup \
  > plexus-backup-$(date +%Y-%m-%d).json

エクスポートには、エンティティ、エッジ、種別、リレーション、ユーザーが含まれます(トークンハッシュは含まれません)。セッション、パスキー、OAuth トークン、個人用トークン、共有トークンは含まれません。認証情報とセッション状態はバックアップされません。?include_audit=1 を渡すと、直近 1000 件のアクティビティログ行を別のファイル名で含められます(個人データを含むため、明示的なオプトインが必要です)。


開発

npm ci
docker compose up -d surrealdb
cp .env.example .env        # fill in required fields
npm run dev                 # live reload on :8787

品質ゲート:

npm run typecheck
npm run build
npx vitest run
npm audit --omit=dev --audit-level=moderate

コミット規約、PR に関する要件、レビュー時に適用する設計原則については、CONTRIBUTING.md を参照してください。


セキュリティ

  • Markdown は許可リスト方式の URL パーサーでレンダリングされます。リンクには rel="noopener nofollow ugc" が付与されます。
  • 属性ペイロードの上限は 64 KB で、Zod の境界で拒否されます。プロトタイプキー(__proto__、constructor、prototype)は再帰的に除去されます。
  • トークン生成では、剰余バイアスを避けるため、base62 に対する棄却サンプリングを使用します。
  • WebAuthn チャレンジでは、機密性の高い操作に目的のバインドを適用し、追加認証を求めます。
  • ログインエラー時は、(ユーザー、パスキー、レート制限の)どの要因が失敗した場合も一律に LOGIN_FAIL を返し、ユーザーの存在を特定できないようにします。
  • kind=secret は MCP 経由ではアクセスできず、共有もできません。ダッシュボードでは管理者のみが、追加認証用パスキーを使ってアクセスできます。

脆弱性は非公開で報告してください。詳細は SECURITY.md を参照してください。セキュリティに関する問題を公開 issue として投稿しないでください。


ライセンス

Apache License 2.0。詳細は LICENSE を参照してください。

plexus と pcas.io は pcas.io プロジェクトの商標です。Apache ライセンスはコードを対象とするものであり、商標権を付与するものではありません。


謝辞

plexus は、SurrealDB、Hono、Model Context Protocol SDK、SimpleWebAuthn、Zod、D3を基盤として構築されています。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。