@hienlh/ppm 0.8.87 → 0.8.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -40
- package/dist/web/assets/{_basePickBy-5PGDJbfF.js → _basePickBy-3Xe18azI.js} +1 -1
- package/dist/web/assets/{_baseUniq-BT4Ow4Kk.js → _baseUniq-Yy35llnn.js} +1 -1
- package/dist/web/assets/{api-settings-Bx1GaNmQ.js → api-settings-Dh4oFOpX.js} +1 -1
- package/dist/web/assets/{arc-BAOivWpI.js → arc-B9n1Gvb5.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-CFzkFKEL.js +1 -0
- package/dist/web/assets/{architectureDiagram-2XIMDMQ5-DWBCPMLF.js → architectureDiagram-2XIMDMQ5-DqAZP_F6.js} +1 -1
- package/dist/web/assets/{blockDiagram-WCTKOSBZ-TEF8Ally.js → blockDiagram-WCTKOSBZ-h3cDF2vI.js} +1 -1
- package/dist/web/assets/{browser-tab-DaHGm_0i.js → browser-tab-DJLH0eDY.js} +1 -1
- package/dist/web/assets/{c4Diagram-IC4MRINW-dV22iAsY.js → c4Diagram-IC4MRINW--pF1r5lr.js} +1 -1
- package/dist/web/assets/channel-C2fMafck.js +1 -0
- package/dist/web/assets/chat-tab-C8HFXqGS.js +8 -0
- package/dist/web/assets/{chunk-4BX2VUAB-D4tOov49.js → chunk-4BX2VUAB-C3aZvW7B.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-DJ6BynZ4.js → chunk-55IACEB6-D5cABeB9.js} +1 -1
- package/dist/web/assets/{chunk-7E7YKBS2-CiyUJxNI.js → chunk-7E7YKBS2-CkFGv6Zs.js} +1 -1
- package/dist/web/assets/{chunk-7R4GIKGN-BbIFzsIv.js → chunk-7R4GIKGN-Dvbyu4Zw.js} +2 -2
- package/dist/web/assets/{chunk-C72U2L5F-D21mS_6G.js → chunk-C72U2L5F-CtqKiH4q.js} +1 -1
- package/dist/web/assets/{chunk-EGIJ26TM-DzqmU2Z7.js → chunk-EGIJ26TM-Cpr87sBR.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-DXncblvW.js → chunk-FMBD7UC4-D23YVTOU.js} +1 -1
- package/dist/web/assets/{chunk-GEFDOKGD-BbQkJu8C.js → chunk-GEFDOKGD-tDjHsAUs.js} +1 -1
- package/dist/web/assets/chunk-GLR3WWYH-DBdWQ3zy.js +2 -0
- package/dist/web/assets/chunk-HHEYEP7N-BBw_z0fW.js +1 -0
- package/dist/web/assets/{chunk-JSJVCQXG-23tyvw8k.js → chunk-JSJVCQXG-BBmymCjA.js} +1 -1
- package/dist/web/assets/{chunk-KX2RTZJC-sQ0o-39C.js → chunk-KX2RTZJC-DP36BDiU.js} +1 -1
- package/dist/web/assets/{chunk-KYZI473N-BcUZNnwd.js → chunk-KYZI473N-Djw13C-3.js} +1 -1
- package/dist/web/assets/{chunk-L3YUKLVL-C7qGJrfV.js → chunk-L3YUKLVL-HG_eMj_C.js} +1 -1
- package/dist/web/assets/{chunk-MX3YWQON-BpS_PtKp.js → chunk-MX3YWQON-C2UEioMs.js} +1 -1
- package/dist/web/assets/{chunk-NQ4KR5QH-wMgTlP7f.js → chunk-NQ4KR5QH-DXUTQ-BL.js} +1 -1
- package/dist/web/assets/{chunk-O4XLMI2P-JC6EGoUz.js → chunk-O4XLMI2P-BsUWb9d0.js} +1 -1
- package/dist/web/assets/{chunk-OZEHJAEY-BXhYx3nO.js → chunk-OZEHJAEY-rG0P22U9.js} +1 -1
- package/dist/web/assets/{chunk-PQ6SQG4A-D6BTbCQw.js → chunk-PQ6SQG4A-DX0xW7kO.js} +1 -1
- package/dist/web/assets/{chunk-PU5JKC2W-Dw8ClWch.js → chunk-PU5JKC2W-C7Gry6md.js} +1 -1
- package/dist/web/assets/chunk-QZHKN3VN-DFKFM_C1.js +1 -0
- package/dist/web/assets/{chunk-R5LLSJPH-CFwSJijQ.js → chunk-R5LLSJPH-CMY0PkRK.js} +1 -1
- package/dist/web/assets/{chunk-WL4C6EOR-DfofndiH.js → chunk-WL4C6EOR-CXuQvlyu.js} +1 -1
- package/dist/web/assets/{chunk-XIRO2GV7-Djlmrely.js → chunk-XIRO2GV7-DRJEb7Zb.js} +1 -1
- package/dist/web/assets/{chunk-XPW4576I-BPQQBakK.js → chunk-XPW4576I-BPEX8KhL.js} +1 -1
- package/dist/web/assets/{chunk-XZSTWKYB-DxAOx4hG.js → chunk-XZSTWKYB-Cb0iqycX.js} +1 -1
- package/dist/web/assets/{chunk-YBOYWFTD-CeU4Q-xC.js → chunk-YBOYWFTD-av5aeHLq.js} +1 -1
- package/dist/web/assets/classDiagram-VBA2DB6C-Dp4Kk3Yb.js +1 -0
- package/dist/web/assets/classDiagram-v2-RAHNMMFH-D8IvcV_B.js +1 -0
- package/dist/web/assets/clone-B2hUek6n.js +1 -0
- package/dist/web/assets/code-editor-CaGdx-lS.js +2 -0
- package/dist/web/assets/{cose-bilkent-S5V4N54A-B_AWZsOP.js → cose-bilkent-S5V4N54A-qudEiMCT.js} +1 -1
- package/dist/web/assets/{csv-preview-DLqYtXxt.js → csv-preview-DUbHtTAS.js} +1 -1
- package/dist/web/assets/{dagre-Dbb5k38K.js → dagre-BFcnKyBF.js} +1 -1
- package/dist/web/assets/{dagre-KLK3FWXG-BH7aWGRP.js → dagre-KLK3FWXG-C3O-MTLf.js} +1 -1
- package/dist/web/assets/{database-viewer-DXk79Nel.js → database-viewer-i4Ddk6mO.js} +1 -1
- package/dist/web/assets/{diagram-E7M64L7V-B1Qz70Do.js → diagram-E7M64L7V-DxPjK7_c.js} +1 -1
- package/dist/web/assets/{diagram-IFDJBPK2-k55eVqVU.js → diagram-IFDJBPK2-sqTog_XV.js} +1 -1
- package/dist/web/assets/{diagram-P4PSJMXO-BkfNRc9U.js → diagram-P4PSJMXO-hzmp0GHK.js} +1 -1
- package/dist/web/assets/diff-viewer-DQDS7yjv.js +4 -0
- package/dist/web/assets/{erDiagram-INFDFZHY-CKzVujYI.js → erDiagram-INFDFZHY-DLeYhAAT.js} +1 -1
- package/dist/web/assets/{flowDiagram-PKNHOUZH-DIqcTrDV.js → flowDiagram-PKNHOUZH-CRxlE9Sr.js} +1 -1
- package/dist/web/assets/{ganttDiagram-A5KZAMGK-D4v7ZbVE.js → ganttDiagram-A5KZAMGK-BdjmoMLS.js} +1 -1
- package/dist/web/assets/git-graph-DUs-TN1u.js +1 -0
- package/dist/web/assets/gitGraph-HDMCJU4V-CNlas3Rz.js +1 -0
- package/dist/web/assets/{gitGraphDiagram-K3NZZRJ6-BTXo57mF.js → gitGraphDiagram-K3NZZRJ6-BeHSX7kk.js} +1 -1
- package/dist/web/assets/{graphlib-BcsNnGcW.js → graphlib-Duh_bWLa.js} +1 -1
- package/dist/web/assets/index-DhtLEnPD.css +2 -0
- package/dist/web/assets/index-Dm6RN1A1.js +37 -0
- package/dist/web/assets/info-3K5VOQVL-BDzTLc11.js +1 -0
- package/dist/web/assets/infoDiagram-LFFYTUFH-ZZmpgc6t.js +2 -0
- package/dist/web/assets/{isEmpty-bnrF3Qbc.js → isEmpty-B9L-Ge-H.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-PHBUUO56-BOyvKMmB.js → ishikawaDiagram-PHBUUO56-Cu0Rt1Ok.js} +1 -1
- package/dist/web/assets/{journeyDiagram-4ABVD52K-ufoasAy6.js → journeyDiagram-4ABVD52K-CgDI-UG4.js} +1 -1
- package/dist/web/assets/{kanban-definition-K7BYSVSG-Bi0UTUeN.js → kanban-definition-K7BYSVSG-h4g10UHL.js} +1 -1
- package/dist/web/assets/keybindings-store-qVLDZz97.js +1 -0
- package/dist/web/assets/{line-B78g-52T.js → line-B75-Rx70.js} +1 -1
- package/dist/web/assets/{linear-DP4mkX3m.js → linear-Bcjv9FQt.js} +1 -1
- package/dist/web/assets/{markdown-renderer-Brj8_LQM.js → markdown-renderer-L1NgC2Rw.js} +5 -5
- package/dist/web/assets/{mermaid-parser.core-DMIWdgEW.js → mermaid-parser.core-8u2leTXI.js} +2 -2
- package/dist/web/assets/{mindmap-definition-YRQLILUH-BsfWvIoO.js → mindmap-definition-YRQLILUH-BaOBwb-W.js} +1 -1
- package/dist/web/assets/{ordinal-_K3x1fkz.js → ordinal-LFEjVtwQ.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-IVa5F-go.js +1 -0
- package/dist/web/assets/pie-UPGHQEXC-CvXHKAzp.js +1 -0
- package/dist/web/assets/{pieDiagram-SKSYHLDU-WP0XXw51.js → pieDiagram-SKSYHLDU-At5Kz0KK.js} +1 -1
- package/dist/web/assets/{postgres-viewer-CwkTGmqy.js → postgres-viewer-_uDispGW.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-337W2JSQ-FHMogtsh.js → quadrantDiagram-337W2JSQ-CdjGIDfw.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-Z-Tr5wtS.js +1 -0
- package/dist/web/assets/{requirementDiagram-Z7DCOOCP-BatTxyWb.js → requirementDiagram-Z7DCOOCP-B9F_Cx_p.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-WA2Y5GQK-ClJuW3Hv.js → sankeyDiagram-WA2Y5GQK-RolPi8bU.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-2WXFIKYE-ByxQqGgs.js → sequenceDiagram-2WXFIKYE-DM-tMAhx.js} +1 -1
- package/dist/web/assets/settings-tab-Bp4041i6.js +1 -0
- package/dist/web/assets/{sqlite-viewer-CFYTwgA8.js → sqlite-viewer-GW-QCjHn.js} +1 -1
- package/dist/web/assets/{stateDiagram-RAJIS63D-f8opcZNY.js → stateDiagram-RAJIS63D-C4EMl6jf.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-FVOUBMTO-B-UjZch3.js +1 -0
- package/dist/web/assets/{tab-store-BJw7OCmy.js → tab-store--SlERlDs.js} +1 -1
- package/dist/web/assets/{terminal-tab-CCDLZA5Y.js → terminal-tab-E4cWujj4.js} +2 -2
- package/dist/web/assets/{timeline-definition-YZTLITO2-58BlOSf9.js → timeline-definition-YZTLITO2-A4PN_Efm.js} +1 -1
- package/dist/web/assets/treemap-KZPCXAKY-C9TYRE0k.js +1 -0
- package/dist/web/assets/{use-monaco-theme-CNzekTN3.js → use-monaco-theme-zABXAAla.js} +1 -1
- package/dist/web/assets/{vennDiagram-LZ73GAT5-BOSy9ma9.js → vennDiagram-LZ73GAT5-ywK7LMaH.js} +1 -1
- package/dist/web/assets/{xychartDiagram-JWTSCODW-z5MVJauZ.js → xychartDiagram-JWTSCODW-DylHYNtJ.js} +1 -1
- package/dist/web/index.html +10 -11
- package/dist/web/sw.js +1 -1
- package/docs/code-standards.md +155 -0
- package/docs/codebase-summary.md +261 -95
- package/docs/project-changelog.md +38 -3
- package/docs/project-roadmap.md +2 -2
- package/docs/system-architecture.md +151 -0
- package/package.json +1 -1
- package/src/providers/claude-agent-sdk.ts +244 -102
- package/src/providers/cli-provider-base.ts +238 -0
- package/src/providers/cursor-cli/cursor-event-mapper.ts +85 -0
- package/src/providers/cursor-cli/cursor-history.ts +207 -0
- package/src/providers/cursor-cli/cursor-provider.ts +146 -0
- package/src/providers/mock-provider.ts +1 -1
- package/src/providers/provider.interface.ts +1 -0
- package/src/providers/registry.ts +43 -4
- package/src/server/index.ts +6 -0
- package/src/server/routes/chat.ts +14 -3
- package/src/server/routes/mcp.ts +84 -0
- package/src/server/routes/settings.ts +14 -0
- package/src/server/ws/chat.ts +127 -81
- package/src/services/chat.service.ts +10 -15
- package/src/services/db.service.ts +8 -0
- package/src/services/mcp-config.service.ts +102 -0
- package/src/types/api.ts +1 -1
- package/src/types/chat.ts +23 -2
- package/src/types/config.ts +33 -11
- package/src/types/mcp.ts +47 -0
- package/src/utils/ndjson-line-parser.ts +36 -0
- package/src/web/components/chat/chat-history-bar.tsx +48 -29
- package/src/web/components/chat/chat-tab.tsx +29 -24
- package/src/web/components/chat/message-input.tsx +64 -5
- package/src/web/components/chat/provider-selector.tsx +150 -0
- package/src/web/components/chat/session-picker.tsx +3 -1
- package/src/web/components/settings/ai-settings-section.tsx +196 -137
- package/src/web/components/settings/mcp-server-dialog.tsx +208 -0
- package/src/web/components/settings/mcp-settings-section.tsx +143 -0
- package/src/web/components/settings/settings-tab.tsx +5 -2
- package/src/web/hooks/use-chat.ts +32 -15
- package/src/web/lib/api-mcp.ts +38 -0
- package/dist/web/assets/architecture-PBZL5I3N-DEO2f3VD.js +0 -1
- package/dist/web/assets/channel-wrd-NHWf.js +0 -1
- package/dist/web/assets/chat-tab-BDYE0KHF.js +0 -8
- package/dist/web/assets/chunk-GLR3WWYH-CzYx4w-r.js +0 -2
- package/dist/web/assets/chunk-HHEYEP7N-HRhYy3kG.js +0 -1
- package/dist/web/assets/chunk-QZHKN3VN-CYaTbeZf.js +0 -1
- package/dist/web/assets/classDiagram-VBA2DB6C-lse8oZoJ.js +0 -1
- package/dist/web/assets/classDiagram-v2-RAHNMMFH-CxkwuInd.js +0 -1
- package/dist/web/assets/clone-LRxlvnMj.js +0 -1
- package/dist/web/assets/code-editor-DTA3c9Y8.js +0 -2
- package/dist/web/assets/diff-viewer-HhIcsOQE.js +0 -4
- package/dist/web/assets/git-graph-CQtWu8yE.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-Bwna3and.js +0 -1
- package/dist/web/assets/index-CgQXpBb_.css +0 -2
- package/dist/web/assets/index-DEeeRoka.js +0 -37
- package/dist/web/assets/info-3K5VOQVL-_vRxVNUm.js +0 -1
- package/dist/web/assets/infoDiagram-LFFYTUFH-B1CX0pbC.js +0 -2
- package/dist/web/assets/input-BglMT33g.js +0 -1
- package/dist/web/assets/keybindings-store-1CJ7VX57.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-DY5PNnZU.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-BHncZutv.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-DH0AOkUy.js +0 -1
- package/dist/web/assets/settings-tab-BDE1MsIh.js +0 -1
- package/dist/web/assets/stateDiagram-v2-FVOUBMTO-DrxVDY9q.js +0 -1
- package/dist/web/assets/treemap-KZPCXAKY-B2Xkyv-K.js +0 -1
- /package/dist/web/assets/{api-client-BfBM3I7n.js → api-client-BKIT_Qeg.js} +0 -0
- /package/dist/web/assets/{array-B9UHiPd-.js → array-DqLCdDFv.js} +0 -0
- /package/dist/web/assets/{chevron-right-DeV0ehiG.js → chevron-right-CHnjJt4E.js} +0 -0
- /package/dist/web/assets/{columns-2-DpsNbZOc.js → columns-2-DbesTfa7.js} +0 -0
- /package/dist/web/assets/{cytoscape.esm-BW-DbntU.js → cytoscape.esm-CWPXKqbJ.js} +0 -0
- /package/dist/web/assets/{defaultLocale-5eAKkKJC.js → defaultLocale-CrJzLgRD.js} +0 -0
- /package/dist/web/assets/{dist-lF8CoYII.js → dist-CALwEtco.js} +0 -0
- /package/dist/web/assets/{dist-CSJdAyA9.js → dist-Cep75xXf.js} +0 -0
- /package/dist/web/assets/{dist-DylI9XxN.js → dist-DGDPTxs1.js} +0 -0
- /package/dist/web/assets/{init-DlZdxViB.js → init-C0r9Gk5G.js} +0 -0
- /package/dist/web/assets/{isArrayLikeObject-B_v2FtYn.js → isArrayLikeObject-CGBoxvCD.js} +0 -0
- /package/dist/web/assets/{katex-Bqvo_ZG0.js → katex-DzXRfQ_m.js} +0 -0
- /package/dist/web/assets/{lib-BQ34Db2e.js → lib-BeaDXEkP.js} +0 -0
- /package/dist/web/assets/{math-069Z4SuC.js → math-y9zN1W-N.js} +0 -0
- /package/dist/web/assets/{path-6uRLdFF7.js → path-DIKpVbHL.js} +0 -0
- /package/dist/web/assets/{preload-helper-uTix4PVD.js → preload-helper-Bf_JiD2A.js} +0 -0
- /package/dist/web/assets/{react-ER-4DN55.js → react-SKk5z-bm.js} +0 -0
- /package/dist/web/assets/{rough.esm-JX0wREDd.js → rough.esm-nHaDi0Kw.js} +0 -0
- /package/dist/web/assets/{src-BqX54PbV.js → src-Dw4QhedI.js} +0 -0
- /package/dist/web/assets/{table-C7X5UAEI.js → table-CQVQM2SB.js} +0 -0
- /package/dist/web/assets/{tag-CCtdV063.js → tag-Q2dZiSPX.js} +0 -0
- /package/dist/web/assets/{utils-BNytJOb1.js → utils-DMiycH3O.js} +0 -0
package/docs/codebase-summary.md
CHANGED
|
@@ -1,103 +1,105 @@
|
|
|
1
1
|
# PPM Codebase Summary
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Last Updated:** 2026-03-26
|
|
4
|
+
**Version:** 0.8.60
|
|
5
|
+
**Repository:** PPM (Project & Process Manager) — Multi-provider web IDE/project manager with Claude Agent SDK
|
|
6
|
+
|
|
7
|
+
**Core Statistics:**
|
|
8
|
+
- **303 files** across CLI, server, web, and test layers
|
|
9
|
+
- **490,667 tokens** total codebase size
|
|
10
|
+
- **492 passing tests** (13 new tests for provider models API)
|
|
11
|
+
- **Tech Stack:** Bun (runtime), Hono (HTTP), React (UI), Claude Agent SDK (AI)
|
|
12
|
+
|
|
13
|
+
---
|
|
4
14
|
|
|
5
15
|
## Directory Structure
|
|
6
16
|
|
|
7
17
|
```
|
|
8
|
-
|
|
9
|
-
├──
|
|
10
|
-
│ ├──
|
|
11
|
-
│
|
|
12
|
-
│
|
|
13
|
-
|
|
14
|
-
│
|
|
15
|
-
│
|
|
16
|
-
│ │
|
|
17
|
-
│
|
|
18
|
-
│ │
|
|
19
|
-
│ │
|
|
20
|
-
│ │
|
|
21
|
-
│ │
|
|
22
|
-
│ │
|
|
23
|
-
│ │
|
|
24
|
-
│ │
|
|
25
|
-
│ │
|
|
26
|
-
│ │
|
|
27
|
-
│ │
|
|
28
|
-
│ ├──
|
|
29
|
-
│ │
|
|
30
|
-
│
|
|
31
|
-
│
|
|
32
|
-
│
|
|
33
|
-
|
|
34
|
-
│
|
|
35
|
-
│
|
|
36
|
-
│
|
|
37
|
-
│ │
|
|
38
|
-
│
|
|
39
|
-
│
|
|
40
|
-
│
|
|
41
|
-
|
|
42
|
-
│
|
|
43
|
-
│
|
|
44
|
-
│
|
|
45
|
-
│
|
|
46
|
-
│
|
|
47
|
-
│ ├──
|
|
48
|
-
│
|
|
49
|
-
│
|
|
50
|
-
│
|
|
51
|
-
│
|
|
52
|
-
│ ├──
|
|
53
|
-
│ │ ├──
|
|
54
|
-
│ │ ├──
|
|
55
|
-
│ │
|
|
56
|
-
│
|
|
57
|
-
|
|
58
|
-
│
|
|
59
|
-
│
|
|
60
|
-
|
|
61
|
-
│
|
|
62
|
-
│
|
|
63
|
-
│
|
|
64
|
-
│
|
|
65
|
-
│
|
|
66
|
-
│
|
|
67
|
-
│
|
|
68
|
-
│
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
│
|
|
75
|
-
│ │
|
|
76
|
-
│ ├──
|
|
77
|
-
│ │ ├──
|
|
78
|
-
│ │
|
|
79
|
-
│ ├──
|
|
80
|
-
│ │
|
|
81
|
-
│
|
|
82
|
-
│ │ ├──
|
|
83
|
-
│ │ ├──
|
|
84
|
-
│ │
|
|
85
|
-
│
|
|
86
|
-
│
|
|
87
|
-
│
|
|
88
|
-
│
|
|
89
|
-
│
|
|
90
|
-
│
|
|
91
|
-
│
|
|
92
|
-
|
|
93
|
-
│ │ ├── panel-store.ts # Grid layout, panel creation/movement, keep-alive snapshots
|
|
94
|
-
│ │ ├── panel-utils.ts # Layout algorithm helpers, grid manipulation
|
|
95
|
-
│ │ ├── file-store.ts # File cache
|
|
96
|
-
│ │ └── settings-store.ts # Theme, sidebar state, git view mode, device name
|
|
97
|
-
│ ├── hooks/ # Custom React hooks (9 files)
|
|
98
|
-
│ │ ├── use-chat.ts # Chat streaming, messages, approvals, context window tracking
|
|
99
|
-
│ │ ├── use-websocket.ts # WebSocket connection with auto-reconnect
|
|
100
|
-
│ │ ├── use-terminal.ts # Terminal connection and streaming
|
|
18
|
+
src/
|
|
19
|
+
├── cli/
|
|
20
|
+
│ ├── commands/ # 13 CLI commands (start, stop, init, config, chat, db, git, etc.)
|
|
21
|
+
│ └── utils/
|
|
22
|
+
│ └── project-resolver.ts # Resolve project name -> path
|
|
23
|
+
├── server/
|
|
24
|
+
│ ├── index.ts # Hono server setup, Bun.serve, WebSocket upgrade
|
|
25
|
+
│ ├── middleware/
|
|
26
|
+
│ │ └── auth.ts # Token validation middleware
|
|
27
|
+
│ ├── routes/
|
|
28
|
+
│ │ ├── settings.ts # GET/PUT /api/settings/ai, GET /api/settings/ai/providers/:id/models
|
|
29
|
+
│ │ ├── chat.ts # Sessions, messages, GET /chat/providers/:providerId/models
|
|
30
|
+
│ │ ├── projects.ts # Project CRUD, reorder, color
|
|
31
|
+
│ │ ├── accounts.ts # Account management (multi-account support)
|
|
32
|
+
│ │ ├── database.ts # DB connection CRUD, schema management
|
|
33
|
+
│ │ ├── git.ts # Git operations (status, commit, log, graph)
|
|
34
|
+
│ │ ├── files.ts # File operations (read, write, tree)
|
|
35
|
+
│ │ ├── mcp.ts # MCP server CRUD + import (GET, POST, PUT, DELETE)
|
|
36
|
+
│ │ ├── upgrade.ts # Version checking, upgrade
|
|
37
|
+
│ │ └── static.ts # Serve frontend (dist/web)
|
|
38
|
+
│ ├── helpers/
|
|
39
|
+
│ │ └── resolve-project.ts # Resolve project from request params
|
|
40
|
+
│ └── ws/
|
|
41
|
+
│ ├── chat.ts # WebSocket chat streaming
|
|
42
|
+
│ └── terminal.ts # WebSocket terminal I/O
|
|
43
|
+
├── providers/ # AI Provider adapters
|
|
44
|
+
│ ├── provider.interface.ts # AIProvider interface (ADDED: listModels?())
|
|
45
|
+
│ ├── claude-agent-sdk.ts # Primary provider (listModels: hardcoded 2 models)
|
|
46
|
+
│ ├── cursor-cli/
|
|
47
|
+
│ │ └── cursor-provider.ts # CLI-based provider (listModels: subprocess with TTL cache)
|
|
48
|
+
│ ├── cli-provider-base.ts # Abstract base for CLI providers
|
|
49
|
+
│ ├── mock-provider.ts # Test provider
|
|
50
|
+
│ └── registry.ts # Provider routing (list() vs listAll())
|
|
51
|
+
├── services/ # Business logic (25+ files)
|
|
52
|
+
│ ├── chat.service.ts # Session/message streaming
|
|
53
|
+
│ ├── config.service.ts # Config loading/persistence
|
|
54
|
+
│ ├── db.service.ts # SQLite CRUD (schema migrations)
|
|
55
|
+
│ ├── file.service.ts # File operations
|
|
56
|
+
│ ├── git.service.ts # Git commands
|
|
57
|
+
│ ├── terminal.service.ts # PTY management
|
|
58
|
+
│ ├── account.service.ts # Account CRUD & encryption
|
|
59
|
+
│ ├── upgrade.service.ts # Version checking, installation
|
|
60
|
+
│ ├── mcp-config.service.ts # MCP server CRUD (list, get, set, remove, import)
|
|
61
|
+
│ ├── database/
|
|
62
|
+
│ │ ├── adapter-registry.ts # SQLite/Postgres adapter registry
|
|
63
|
+
│ │ ├── sqlite-adapter.ts
|
|
64
|
+
│ │ ├── postgres-adapter.ts
|
|
65
|
+
│ │ └── readonly-check.ts # CTE-safe readonly validation
|
|
66
|
+
│ └── ... (20+ other services)
|
|
67
|
+
├── lib/
|
|
68
|
+
│ ├── account-crypto.ts # AES-256 encryption
|
|
69
|
+
│ └── network-utils.ts
|
|
70
|
+
├── types/
|
|
71
|
+
│ ├── chat.ts # Session, Message, ChatEvent, ModelOption, AIProvider
|
|
72
|
+
│ ├── api.ts # ApiResponse envelope
|
|
73
|
+
│ ├── config.ts
|
|
74
|
+
│ ├── database.ts
|
|
75
|
+
│ ├── git.ts
|
|
76
|
+
│ ├── mcp.ts # McpServerConfig, McpTransportType, validation
|
|
77
|
+
│ ├── project.ts
|
|
78
|
+
│ └── terminal.ts
|
|
79
|
+
└── web/ # React frontend (Vite + React 18)
|
|
80
|
+
├── app.tsx # Root component
|
|
81
|
+
├── stores/ # Zustand state (6 stores)
|
|
82
|
+
├── hooks/ # Custom hooks (9 hooks)
|
|
83
|
+
├── components/
|
|
84
|
+
│ ├── chat/
|
|
85
|
+
│ │ ├── chat-tab.tsx
|
|
86
|
+
│ │ ├── message-list.tsx
|
|
87
|
+
│ │ ├── message-input.tsx
|
|
88
|
+
│ │ ├── provider-selector.tsx
|
|
89
|
+
│ │ ├── chat-history-bar.tsx # ADDED: Provider badges, provider-aware usage
|
|
90
|
+
│ │ └── ... (6 other chat components)
|
|
91
|
+
│ ├── settings/
|
|
92
|
+
│ │ ├── ai-settings-section.tsx # UPDATED: Per-provider tabs, dynamic model dropdowns
|
|
93
|
+
│ │ ├── mcp-settings-section.tsx # ADDED: MCP servers tab (list, add, edit, delete)
|
|
94
|
+
│ │ └── mcp-server-dialog.tsx # ADDED: Add/Edit MCP server dialog
|
|
95
|
+
│ ├── database/
|
|
96
|
+
│ ├── editor/
|
|
97
|
+
│ ├── explorer/
|
|
98
|
+
│ ├── git/
|
|
99
|
+
│ ├── layout/
|
|
100
|
+
│ ├── terminal/
|
|
101
|
+
│ └── ui/
|
|
102
|
+
└── lib/
|
|
101
103
|
│ │ ├── use-url-sync.ts # Sync browser URL with active project/tab state
|
|
102
104
|
│ │ ├── use-tab-drag.ts # Tab drag-and-drop logic
|
|
103
105
|
│ │ ├── use-global-keybindings.ts # Global shortcuts (Shift+Shift palette, Alt+[/] tab cycling)
|
|
@@ -107,6 +109,7 @@ ppm/
|
|
|
107
109
|
│ ├── lib/ # Utilities (12 files)
|
|
108
110
|
│ │ ├── api-client.ts # Fetch wrapper with auth token, envelope unwrapping
|
|
109
111
|
│ │ ├── api-settings.ts # AI settings API client (GET/PUT /api/settings/ai)
|
|
112
|
+
│ │ ├── api-mcp.ts # ADDED: MCP settings API client (CRUD + import)
|
|
110
113
|
│ │ ├── ws-client.ts # WebSocket with exponential backoff + Cloudflare handshake
|
|
111
114
|
│ │ ├── file-support.ts # File type detection (language, icons, preview)
|
|
112
115
|
│ │ ├── project-avatar.ts # Smart project initials (collision resolution)
|
|
@@ -370,11 +373,174 @@ UI updates staged/unstaged lists
|
|
|
370
373
|
- PWA manifest, service worker
|
|
371
374
|
- Assets (~500KB gzipped)
|
|
372
375
|
|
|
376
|
+
## Multi-Provider Architecture (v0.8.60)
|
|
377
|
+
|
|
378
|
+
### Dynamic Model Listing Feature
|
|
379
|
+
|
|
380
|
+
**Problem:** Different AI providers expose different models. Claude has hardcoded models, but CLI-based providers (e.g., Cursor) discover models at runtime.
|
|
381
|
+
|
|
382
|
+
**Solution:** Optional `listModels()` method on `AIProvider` interface
|
|
383
|
+
|
|
384
|
+
### Provider Interface
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
// src/types/chat.ts
|
|
388
|
+
export interface ModelOption {
|
|
389
|
+
value: string; // Model ID (e.g., "claude-sonnet-4-6")
|
|
390
|
+
label: string; // Display name (e.g., "Claude Sonnet 4.6")
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
interface AIProvider {
|
|
394
|
+
// Required methods
|
|
395
|
+
createSession(): Promise<Session>;
|
|
396
|
+
sendMessage(sessionId, message, context?): AsyncIterable<ChatEvent>;
|
|
397
|
+
|
|
398
|
+
// Optional methods
|
|
399
|
+
listModels?(): Promise<ModelOption[]>;
|
|
400
|
+
isAvailable?(): Promise<boolean>;
|
|
401
|
+
// ... (5 other optional methods)
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Provider Implementations
|
|
406
|
+
|
|
407
|
+
#### Claude (agent-sdk.ts)
|
|
408
|
+
- `listModels()` returns hardcoded 2 models: Sonnet 4.6, Opus 4.6
|
|
409
|
+
- Direct implementation (no subprocess)
|
|
410
|
+
|
|
411
|
+
#### Cursor (cursor-provider.ts)
|
|
412
|
+
- `listModels()` runs `cursor-agent --list-models` subprocess
|
|
413
|
+
- 5-minute TTL cache (prevents repeated subprocess calls)
|
|
414
|
+
- 10-second timeout (graceful fallback to empty list)
|
|
415
|
+
- Extends `CliProvider` abstract base
|
|
416
|
+
|
|
417
|
+
#### Mock (mock-provider.ts)
|
|
418
|
+
- For testing; returns canned models
|
|
419
|
+
|
|
420
|
+
### API Endpoints
|
|
421
|
+
|
|
422
|
+
**Global Models Endpoint** (`GET /api/settings/ai/providers/:id/models`)
|
|
423
|
+
```typescript
|
|
424
|
+
// Used in Settings UI (no project context needed)
|
|
425
|
+
settingsRoutes.get("/ai/providers/:id/models", async (c) => {
|
|
426
|
+
const provider = providerRegistry.get(c.req.param("id"));
|
|
427
|
+
const models = await provider.listModels?.() ?? [];
|
|
428
|
+
return c.json(ok(models));
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
**Project-Scoped Models Endpoint** (`GET /api/project/:name/chat/providers/:providerId/models`)
|
|
433
|
+
```typescript
|
|
434
|
+
// Used in Chat tab (scoped to project for consistency)
|
|
435
|
+
chatRoutes.get("/providers/:providerId/models", async (c) => {
|
|
436
|
+
const provider = providerRegistry.get(c.req.param("providerId"));
|
|
437
|
+
const models = await provider.listModels?.() ?? [];
|
|
438
|
+
return c.json(ok(models));
|
|
439
|
+
});
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Provider Registry Pattern
|
|
443
|
+
|
|
444
|
+
**list() — User-facing providers:**
|
|
445
|
+
```typescript
|
|
446
|
+
list(): ProviderInfo[] {
|
|
447
|
+
return [
|
|
448
|
+
{ id: "claude", name: "Claude" },
|
|
449
|
+
{ id: "cursor", name: "Cursor" }
|
|
450
|
+
// mock excluded
|
|
451
|
+
];
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
**listAll() — All providers (internal):**
|
|
456
|
+
```typescript
|
|
457
|
+
listAll(): ProviderInfo[] {
|
|
458
|
+
return [..., { id: "mock", name: "Mock" }];
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Auto-Bootstrap:**
|
|
463
|
+
```typescript
|
|
464
|
+
// On startup, detect CLI providers
|
|
465
|
+
async bootstrapProviders() {
|
|
466
|
+
const cursor = this.providers.get("cursor");
|
|
467
|
+
if (cursor && await cursor.isAvailable?.()) {
|
|
468
|
+
// Auto-create config entry if detected
|
|
469
|
+
// Save config (only if new)
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### UI Components
|
|
475
|
+
|
|
476
|
+
#### AI Settings Section (ai-settings-section.tsx) — UPDATED
|
|
477
|
+
- Per-provider tabs (Claude, Cursor, etc.)
|
|
478
|
+
- Dynamic model dropdowns fetched from `/api/settings/ai/providers/:id/models`
|
|
479
|
+
- Fallback to hardcoded models if API call fails
|
|
480
|
+
- Provider-aware settings (SDK vs CLI options)
|
|
481
|
+
|
|
482
|
+
#### Chat History Bar (chat-history-bar.tsx) — ADDED
|
|
483
|
+
- Provider badges showing active provider for each session
|
|
484
|
+
- Provider-aware usage display:
|
|
485
|
+
- **Claude:** Full stats `(tokens_in:X, tokens_out:Y, cost: $Z)`
|
|
486
|
+
- **Other:** Context-only `(tokens: X)`
|
|
487
|
+
|
|
488
|
+
### Configuration
|
|
489
|
+
|
|
490
|
+
```yaml
|
|
491
|
+
ai:
|
|
492
|
+
default_provider: claude
|
|
493
|
+
providers:
|
|
494
|
+
claude:
|
|
495
|
+
type: agent-sdk
|
|
496
|
+
model: claude-sonnet-4-6 # from listModels()
|
|
497
|
+
effort: high
|
|
498
|
+
max_turns: 100
|
|
499
|
+
cursor:
|
|
500
|
+
type: cli
|
|
501
|
+
model: cursor-fast # from listModels()
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Testing
|
|
505
|
+
|
|
506
|
+
**New Integration Tests (13 tests):**
|
|
507
|
+
- `provider-models-api.test.ts` — Model API endpoints
|
|
508
|
+
- `chat-service-multi-provider.test.ts` — Multi-provider flows
|
|
509
|
+
- `cursor-provider.test.ts` — Subprocess TTL cache, timeout handling
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
373
513
|
## Testing Strategy
|
|
374
514
|
|
|
375
515
|
| Test Type | Location | Coverage |
|
|
376
516
|
|-----------|----------|----------|
|
|
377
517
|
| Unit | tests/unit/ | Services, utilities |
|
|
378
|
-
| Integration | tests/integration/ | API routes, WebSocket |
|
|
518
|
+
| Integration | tests/integration/ | API routes, WebSocket, provider models |
|
|
379
519
|
| E2E | None yet | Planned for v3 |
|
|
380
520
|
|
|
521
|
+
**Key Gotchas:**
|
|
522
|
+
- Test DB isolated per test (never writes to ~/.ppm/ppm.db)
|
|
523
|
+
- Auth disabled in test mode (test-setup.ts)
|
|
524
|
+
- Mock provider used for deterministic responses
|
|
525
|
+
- 492 passing tests (0 failures, v0.8.60)
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## Recent Changes (v0.8.60)
|
|
530
|
+
|
|
531
|
+
### Added
|
|
532
|
+
- **Dynamic Model Listing** — `listModels?()` on AIProvider interface
|
|
533
|
+
- **Provider Models APIs** — Global and project-scoped endpoints
|
|
534
|
+
- **AI Settings UI** — Per-provider tabs with dynamic model dropdowns
|
|
535
|
+
- **Chat History Badges** — Provider-aware usage display
|
|
536
|
+
- **13 new integration tests** for provider models API
|
|
537
|
+
|
|
538
|
+
### Technical Details
|
|
539
|
+
- `ModelOption` type: `{ value: string; label: string }`
|
|
540
|
+
- Claude: Hardcoded 2 models
|
|
541
|
+
- Cursor: Subprocess with 5-min TTL cache, 10s timeout
|
|
542
|
+
- Registry: `list()` vs `listAll()` distinction
|
|
543
|
+
- Bootstrap: Auto-detect CLI providers on startup
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to PPM are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|
4
4
|
|
|
5
|
-
**Current Version:** v0.8.
|
|
5
|
+
**Current Version:** v0.8.87
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
## [0.8.77] — 2026-04-01
|
|
9
|
+
## [0.8.77] — 2026-04-01
|
|
10
10
|
|
|
11
11
|
### Added
|
|
12
12
|
- **Deterministic Tab URLs + Backend Workspace Sync**
|
|
@@ -41,6 +41,41 @@ All notable changes to PPM are documented here. Format follows [Keep a Changelog
|
|
|
41
41
|
|
|
42
42
|
---
|
|
43
43
|
|
|
44
|
+
## [0.8.63] — 2026-03-28
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
- **MCP Server Management** — Configure Model Context Protocol servers via Settings UI
|
|
48
|
+
- REST API: GET/POST/PUT/DELETE `/api/settings/mcp`, plus import endpoints
|
|
49
|
+
- Storage: SQLite `mcp_servers` table (name, transport, config JSON)
|
|
50
|
+
- UI: Settings tab with server list, add/edit dialog, delete action
|
|
51
|
+
- Auto-import: Reads `~/.claude.json` on first access (skips existing/invalid)
|
|
52
|
+
- Validation: Name (alphanumeric, max 50 chars) + transport-specific config checks
|
|
53
|
+
- SDK integration: Servers passed to `query()` as `mcpServers`, tools auto-allowed via `mcp__*` wildcard
|
|
54
|
+
|
|
55
|
+
### Technical Details
|
|
56
|
+
- **Files Created:**
|
|
57
|
+
- `src/types/mcp.ts` — McpServerConfig types, validation functions
|
|
58
|
+
- `src/services/mcp-config.service.ts` — CRUD service + bulk import
|
|
59
|
+
- `src/server/routes/mcp.ts` — REST API endpoints
|
|
60
|
+
- `src/web/lib/api-mcp.ts` — Frontend API client
|
|
61
|
+
- `src/web/components/settings/mcp-settings-section.tsx` — Settings UI
|
|
62
|
+
- `src/web/components/settings/mcp-server-dialog.tsx` — Add/Edit dialog
|
|
63
|
+
- **Files Modified:**
|
|
64
|
+
- `src/services/db.service.ts` — Schema v8 migration (mcp_servers table)
|
|
65
|
+
- `src/server/index.ts` — Route registration
|
|
66
|
+
- `src/providers/claude-agent-sdk.ts` — mcpServers + mcp__* allowedTools
|
|
67
|
+
- `src/web/components/settings/settings-tab.tsx` — MCP category added
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## [0.8.62] — 2026-03-26
|
|
72
|
+
|
|
73
|
+
### Added
|
|
74
|
+
- **Cmd+Shift+V shortcut** — Command palette entry for voice input
|
|
75
|
+
- **Voice input** — Web Speech API integration for chat
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
44
79
|
## [0.8.61] — 2026-03-26 (Beta)
|
|
45
80
|
|
|
46
81
|
### Added
|
|
@@ -76,7 +111,7 @@ All notable changes to PPM are documented here. Format follows [Keep a Changelog
|
|
|
76
111
|
- `src/services/chat.service.ts` — Use optional methods instead of duck-typing
|
|
77
112
|
- `src/web/components/chat/session-picker.tsx` — Integrated provider selector
|
|
78
113
|
- **Breaking Changes:** None (backward compatible, all tests passing)
|
|
79
|
-
- **Architecture:** All phases complete (6/6),
|
|
114
|
+
- **Architecture:** All phases complete (6/6), 555 tests passing
|
|
80
115
|
|
|
81
116
|
### Benefits
|
|
82
117
|
- Extensible foundation for Codex, Gemini, and future providers (~100-150 lines each)
|
package/docs/project-roadmap.md
CHANGED
|
@@ -58,13 +58,13 @@ PPM is the **lightest path from phone to code** — a self-hosted, BYOK, multi-d
|
|
|
58
58
|
|
|
59
59
|
**Theme:** Multi-provider AI (Claude + Cursor) + extension system. Ship a focused release, expand providers later.
|
|
60
60
|
|
|
61
|
-
**Overall progress: ~
|
|
61
|
+
**Overall progress: ~53%** (2/3 features complete, merge all at 100%)
|
|
62
62
|
|
|
63
63
|
| Feature | Priority | Status | Description |
|
|
64
64
|
|---------|----------|--------|-------------|
|
|
65
65
|
| **Multi-provider AI** | Critical | ✅ Done | ProviderInterface, registry, Cursor CLI, CLI provider base, UI provider/model selector, permission mode selector, system prompt customization, comprehensive tests — all on beta branch. |
|
|
66
|
+
| **MCP Management** | Medium | ✅ Done | REST API (CRUD + import), SQLite storage, Settings UI, auto-import from `~/.claude.json`, validation, SDK integration. |
|
|
66
67
|
| **Extension architecture** | High | 🔴 0% | Dynamic extension loading. Extensions = npm packages. First extension: extract DB viewer from core. Extension API: register routes, UI panels, sidebar tabs, skills. Config: `"extensions": ["@ppm/ext-database", "@ppm/ext-docker"]`. |
|
|
67
|
-
| **MCP Management** | Medium | 🔴 0% | UI to add/remove/configure MCP servers. Test connection. Per-project MCP overrides. Store in SQLite. Pass to Agent SDK via `mcpServers`. |
|
|
68
68
|
|
|
69
69
|
**Multi-provider — v0.9 scope (reduced):**
|
|
70
70
|
- Tier 1 (full agentic): Claude Agent SDK — file edit, terminal, git, full autonomy
|
|
@@ -977,6 +977,157 @@ ppm db data <name> <table> # Show table data (paginated)
|
|
|
977
977
|
|
|
978
978
|
---
|
|
979
979
|
|
|
980
|
+
## MCP Server Management
|
|
981
|
+
|
|
982
|
+
### Overview
|
|
983
|
+
MCP (Model Context Protocol) servers extend Claude with custom tools and resources. PPM manages MCP server configurations via Settings UI, storing them in SQLite and passing them to the Claude Agent SDK.
|
|
984
|
+
|
|
985
|
+
**Features:**
|
|
986
|
+
- **Add/Edit/Delete** MCP servers via Settings UI
|
|
987
|
+
- **Auto-import** from `~/.claude.json` on first access (convenience, no forced import)
|
|
988
|
+
- **Three transport types:** stdio, HTTP, SSE
|
|
989
|
+
- **Validation** on name and config before storage
|
|
990
|
+
- **SDK integration:** Servers passed to `query()` as `mcpServers` object, tools auto-allowed via `mcp__*` wildcard
|
|
991
|
+
|
|
992
|
+
### Storage Schema
|
|
993
|
+
|
|
994
|
+
```sql
|
|
995
|
+
CREATE TABLE mcp_servers (
|
|
996
|
+
name TEXT PRIMARY KEY,
|
|
997
|
+
transport TEXT NOT NULL DEFAULT 'stdio', -- 'stdio' | 'http' | 'sse'
|
|
998
|
+
config TEXT NOT NULL, -- JSON: McpServerConfig
|
|
999
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
1000
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
1001
|
+
);
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
**Config Format (JSON):**
|
|
1005
|
+
```json
|
|
1006
|
+
{
|
|
1007
|
+
"type": "stdio",
|
|
1008
|
+
"command": "path/to/server",
|
|
1009
|
+
"args": ["--flag"],
|
|
1010
|
+
"env": { "VAR": "value" }
|
|
1011
|
+
}
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
Or HTTP/SSE:
|
|
1015
|
+
```json
|
|
1016
|
+
{
|
|
1017
|
+
"type": "http",
|
|
1018
|
+
"url": "http://localhost:3000",
|
|
1019
|
+
"headers": { "Authorization": "Bearer token" }
|
|
1020
|
+
}
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
### REST API
|
|
1024
|
+
|
|
1025
|
+
**Endpoints** (`src/server/routes/mcp.ts`):
|
|
1026
|
+
|
|
1027
|
+
| Method | Endpoint | Description |
|
|
1028
|
+
|--------|----------|-------------|
|
|
1029
|
+
| **GET** | `/api/settings/mcp` | List all servers; auto-import on first access |
|
|
1030
|
+
| **GET** | `/api/settings/mcp/:name` | Get single server config |
|
|
1031
|
+
| **POST** | `/api/settings/mcp` | Add new server (validates name + config) |
|
|
1032
|
+
| **PUT** | `/api/settings/mcp/:name` | Update existing server |
|
|
1033
|
+
| **DELETE** | `/api/settings/mcp/:name` | Remove server |
|
|
1034
|
+
| **GET** | `/api/settings/mcp/import/preview` | Preview servers in `~/.claude.json` |
|
|
1035
|
+
| **POST** | `/api/settings/mcp/import` | Bulk import from `~/.claude.json` |
|
|
1036
|
+
|
|
1037
|
+
**Add Server Example:**
|
|
1038
|
+
```bash
|
|
1039
|
+
POST /api/settings/mcp
|
|
1040
|
+
Content-Type: application/json
|
|
1041
|
+
|
|
1042
|
+
{
|
|
1043
|
+
"name": "file-server",
|
|
1044
|
+
"config": {
|
|
1045
|
+
"type": "stdio",
|
|
1046
|
+
"command": "/usr/local/bin/file-server",
|
|
1047
|
+
"args": ["--port", "8000"]
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
### Service Layer
|
|
1053
|
+
|
|
1054
|
+
**McpConfigService** (`src/services/mcp-config.service.ts`):
|
|
1055
|
+
- `list()` — Record<name, McpServerConfig> (SDK-compatible format)
|
|
1056
|
+
- `listWithMeta()` — Array with metadata (for UI)
|
|
1057
|
+
- `get(name)` — Single server config
|
|
1058
|
+
- `set(name, config)` — Add or update (upsert)
|
|
1059
|
+
- `remove(name)` — Delete server
|
|
1060
|
+
- `exists(name)` — Check if name exists
|
|
1061
|
+
- `bulkImport(servers)` — Transactional import from `~/.claude.json`, skips existing/invalid
|
|
1062
|
+
|
|
1063
|
+
**Validation:**
|
|
1064
|
+
- `validateMcpName(name)` — alphanumeric + hyphens/underscores, max 50 chars
|
|
1065
|
+
- `validateMcpConfig(config)` — type-specific checks (command for stdio, url for http/sse)
|
|
1066
|
+
|
|
1067
|
+
### Frontend Integration
|
|
1068
|
+
|
|
1069
|
+
**UI Components:**
|
|
1070
|
+
- `MCP Settings Section` (`src/web/components/settings/mcp-settings-section.tsx`) — Tab in Settings UI
|
|
1071
|
+
- `MCP Server Dialog` (`src/web/components/settings/mcp-server-dialog.tsx`) — Add/Edit modal
|
|
1072
|
+
- `API client` (`src/web/lib/api-mcp.ts`) — Fetch/mutate operations
|
|
1073
|
+
|
|
1074
|
+
**Workflow:**
|
|
1075
|
+
1. User opens Settings → MCP tab
|
|
1076
|
+
2. **GET** `/api/settings/mcp` (auto-imports on first access)
|
|
1077
|
+
3. Display list with transport badge + actions (edit, delete)
|
|
1078
|
+
4. Click "Add" → Dialog with name + transport selector + config fields
|
|
1079
|
+
5. **POST** to `/api/settings/mcp` or **PUT** to update
|
|
1080
|
+
6. On success, list refreshes
|
|
1081
|
+
|
|
1082
|
+
### SDK Integration
|
|
1083
|
+
|
|
1084
|
+
**Claude Agent SDK Provider** (`src/providers/claude-agent-sdk.ts`):
|
|
1085
|
+
```typescript
|
|
1086
|
+
// Line ~574
|
|
1087
|
+
const mcpServers = mcpConfigService.list();
|
|
1088
|
+
const hasMcp = Object.keys(mcpServers).length > 0;
|
|
1089
|
+
|
|
1090
|
+
// Line ~589: Pass to query() if servers exist
|
|
1091
|
+
const mcpTools = ["mcp__*"];
|
|
1092
|
+
const queryConfig = {
|
|
1093
|
+
// ... other options
|
|
1094
|
+
...(hasMcp && { mcpServers }),
|
|
1095
|
+
allowedTools: [...otherTools, ...mcpTools],
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1098
|
+
const query = new Query(messages, queryConfig);
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
**Tool Allow List:**
|
|
1102
|
+
- All MCP tools automatically allowed via wildcard `mcp__*`
|
|
1103
|
+
- MCP server connection failures don't block chat (logged as warning)
|
|
1104
|
+
|
|
1105
|
+
### Import Flow
|
|
1106
|
+
|
|
1107
|
+
**Auto-import on first access:**
|
|
1108
|
+
1. GET `/api/settings/mcp` called
|
|
1109
|
+
2. If table is empty, read `~/.claude.json`
|
|
1110
|
+
3. If `mcpServers` key exists, bulk import (validate + skip duplicates)
|
|
1111
|
+
4. Return populated list
|
|
1112
|
+
|
|
1113
|
+
**Manual import:**
|
|
1114
|
+
1. GET `/api/settings/mcp/import/preview` — show what's available
|
|
1115
|
+
2. POST `/api/settings/mcp/import` — import validated servers
|
|
1116
|
+
3. Returns `{ imported: N, skipped: M }`
|
|
1117
|
+
|
|
1118
|
+
### Error Handling
|
|
1119
|
+
|
|
1120
|
+
| Scenario | Response |
|
|
1121
|
+
|----------|----------|
|
|
1122
|
+
| Invalid name (non-alphanumeric) | 400 Bad Request |
|
|
1123
|
+
| Invalid config (missing required fields) | 400 Bad Request |
|
|
1124
|
+
| Duplicate name | 409 Conflict |
|
|
1125
|
+
| Server not found (GET/:name, PUT/:name, DELETE/:name) | 404 Not Found |
|
|
1126
|
+
| `~/.claude.json` not found (import) | 404 Not Found |
|
|
1127
|
+
| Corrupt config JSON (recovery) | Log warning, skip entry, continue |
|
|
1128
|
+
|
|
1129
|
+
---
|
|
1130
|
+
|
|
980
1131
|
## Deployment Architecture
|
|
981
1132
|
|
|
982
1133
|
### Single-Machine Deployment (Current)
|