specrails-desktop 2.16.0 → 2.17.0
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/client/dist/assets/{ActivityFeedPage-C_clKk8d.js → ActivityFeedPage-BLDMBgwL.js} +1 -1
- package/client/dist/assets/AgentsPage-CFaIiyAA.js +86 -0
- package/client/dist/assets/{AnalyticsPage-DZkVU3yd.js → AnalyticsPage-DY8ECq2R.js} +1 -1
- package/client/dist/assets/{BarChart-Dq1h8eYR.js → BarChart-ouivqe5L.js} +2 -2
- package/client/dist/assets/CodePage-DAFtRafz.js +2 -0
- package/client/dist/assets/{DesktopAnalyticsPage-CYhXhJoQ.js → DesktopAnalyticsPage-DIMhuuv0.js} +1 -1
- package/client/dist/assets/{DocsDialog-CS3Syf61.js → DocsDialog-DBYhv3SM.js} +2 -2
- package/client/dist/assets/{DocsPage-l3ZcTO2E.js → DocsPage-DeLRn8W_.js} +2 -2
- package/client/dist/assets/{ExportDropdown-DdB2ncYh.js → ExportDropdown-2XwQ08sz.js} +1 -1
- package/client/dist/assets/{IntegrationsPage-CJ_Om1zu.js → IntegrationsPage-DB-a4FDV.js} +3 -3
- package/client/dist/assets/JobDetailPage-C3lljMiV.js +16 -0
- package/client/dist/assets/JobsPage-CzquwJ6G.js +1 -0
- package/client/dist/assets/LoopBuilderPage-TkoalfW5.js +7 -0
- package/client/dist/assets/LoopsPage-ypooVAQ5.js +1 -0
- package/client/dist/assets/{agents-CQlu1nOm.js → agents-4fnKlwcG.js} +1 -1
- package/client/dist/assets/{agents-DcoUUFZl.js → agents-B-rVIhuw.js} +1 -1
- package/client/dist/assets/{agents-DVYpVGur.js → agents-BRUz6RJs.js} +1 -1
- package/client/dist/assets/{agents-DCX9D3-Q.js → agents-CZQvKDIb.js} +1 -1
- package/client/dist/assets/{agents-BYjUOrLD.js → agents-CuRY-Zg_.js} +1 -1
- package/client/dist/assets/{agents-Jc-CjBDB.js → agents-D7nWnLg6.js} +1 -1
- package/client/dist/assets/{agents-CIhRSOna.js → agents-DzhSlNZd.js} +1 -1
- package/client/dist/assets/{agents-1lhqEcXH.js → agents-E-42N3b1.js} +1 -1
- package/client/dist/assets/{cssMode-DSHXamUH.js → cssMode-BijCvCxs.js} +1 -1
- package/client/dist/assets/{dist-js-Ele_sHO7.js → dist-js-D7m9--SN.js} +1 -1
- package/client/dist/assets/{dist-js-D27I-bfi.js → dist-js-nZhsq1qo.js} +1 -1
- package/client/dist/assets/{editor.main-Cd8qBRAT.js → editor.main-DMIcqwXb.js} +2 -2
- package/client/dist/assets/{freemarker2-CkEKTBm2.js → freemarker2-CP2AizpZ.js} +1 -1
- package/client/dist/assets/{handlebars-BRGx0JpL.js → handlebars-8ElanPOm.js} +1 -1
- package/client/dist/assets/{html-BfRIaGgN.js → html-Mu3b73tG.js} +1 -1
- package/client/dist/assets/{htmlMode-BTpSnsDE.js → htmlMode-tHprGCEv.js} +1 -1
- package/client/dist/assets/index-CDEQ4aU_.js +144 -0
- package/client/dist/assets/{javascript-DMmE2kKN.js → javascript-DlG0apUe.js} +1 -1
- package/client/dist/assets/{jsonMode-hYuaUleY.js → jsonMode-Cr0T4SXO.js} +1 -1
- package/client/dist/assets/{lib-qdRfWwE1.js → lib-Bu76JzdB.js} +2 -2
- package/client/dist/assets/{liquid-C-VbuTr7.js → liquid-QgTgbClC.js} +1 -1
- package/client/dist/assets/{lspLanguageFeatures-BALx-vjn.js → lspLanguageFeatures-D-BxkRza.js} +1 -1
- package/client/dist/assets/mcp-BSCAtU6Y.js +1 -0
- package/client/dist/assets/mcp-Cb6fUaFJ.js +1 -0
- package/client/dist/assets/mcp-CyP0quZ7.js +1 -0
- package/client/dist/assets/mcp-D-zQCO5E.js +1 -0
- package/client/dist/assets/mcp-DCTNWpfB.js +1 -0
- package/client/dist/assets/mcp-DEFGJ1OJ.js +1 -0
- package/client/dist/assets/mcp-DFG9OEXM.js +1 -0
- package/client/dist/assets/mcp-DyERgHA2.js +1 -0
- package/client/dist/assets/{mdx-Cs8MNCzn.js → mdx-B2QTaeRe.js} +1 -1
- package/client/dist/assets/{monaco.contribution-CIvCAJs4.js → monaco.contribution-CkdGfXCC.js} +2 -2
- package/client/dist/assets/{python-CGShLxuI.js → python-BlF0qnMQ.js} +1 -1
- package/client/dist/assets/{razor-CvOBQGd5.js → razor--3PSy3lb.js} +1 -1
- package/client/dist/assets/setup-BiQ7ut8d.js +1 -0
- package/client/dist/assets/setup-BxvjG5Xp.js +1 -0
- package/client/dist/assets/setup-CQ-F6Ciq.js +1 -0
- package/client/dist/assets/setup-D2JlpJEz.js +1 -0
- package/client/dist/assets/setup-DG1R6J6o.js +1 -0
- package/client/dist/assets/setup-DuFJZJsm.js +1 -0
- package/client/dist/assets/setup-DyJszDqm.js +1 -0
- package/client/dist/assets/setup-Ynw19V8w.js +1 -0
- package/client/dist/assets/{tsMode-04uOpiiP.js → tsMode-CHfsvcOI.js} +1 -1
- package/client/dist/assets/{typescript-CX0CwyIZ.js → typescript-Bjxf6BOp.js} +1 -1
- package/client/dist/assets/{upload-DPG87J4t.js → upload-x42OZJr2.js} +1 -1
- package/client/dist/assets/{value-BRVOAvhY.js → value-fEDhg5Bd.js} +1 -1
- package/client/dist/assets/{workers-yr2u4c3C.js → workers-CAAvSda9.js} +1 -1
- package/client/dist/assets/{xml-C0OFlt5X.js → xml-iYCSJ4oL.js} +1 -1
- package/client/dist/assets/{yaml-DgabaPs5.js → yaml-DtO-_yDY.js} +1 -1
- package/client/dist/index.html +10 -9
- package/docs/README.md +2 -0
- package/docs/guide/de/integrations/5-mcp-server.md +74 -0
- package/docs/guide/en/integrations/5-mcp-server.md +74 -0
- package/docs/guide/es/integrations/5-mcp-server.md +74 -0
- package/docs/guide/fr/integrations/5-mcp-server.md +74 -0
- package/docs/guide/it/integrations/5-mcp-server.md +74 -0
- package/docs/guide/ja/integrations/5-mcp-server.md +74 -0
- package/docs/guide/pt/integrations/5-mcp-server.md +74 -0
- package/docs/guide/zh/integrations/5-mcp-server.md +74 -0
- package/docs/mcp.md +249 -0
- package/package.json +8 -5
- package/server/dist/index.js +30 -0
- package/server/dist/mcp/guide.js +82 -0
- package/server/dist/mcp/index.js +21 -0
- package/server/dist/mcp/mcp-admin-router.js +71 -0
- package/server/dist/mcp/mcp-server.js +143 -0
- package/server/dist/mcp/mcp-tiers.js +48 -0
- package/server/dist/mcp/mcp-token.js +119 -0
- package/server/dist/mcp/resources.js +24 -0
- package/server/dist/mcp/tools/agents.js +319 -0
- package/server/dist/mcp/tools/analytics.js +168 -0
- package/server/dist/mcp/tools/app-settings.js +81 -0
- package/server/dist/mcp/tools/catalog.js +45 -0
- package/server/dist/mcp/tools/chat.js +147 -0
- package/server/dist/mcp/tools/code.js +145 -0
- package/server/dist/mcp/tools/jira.js +222 -0
- package/server/dist/mcp/tools/jobs.js +258 -0
- package/server/dist/mcp/tools/loops.js +229 -0
- package/server/dist/mcp/tools/meta.js +96 -0
- package/server/dist/mcp/tools/plugins.js +83 -0
- package/server/dist/mcp/tools/projects.js +62 -0
- package/server/dist/mcp/tools/rails.js +181 -0
- package/server/dist/mcp/tools/setup.js +187 -0
- package/server/dist/mcp/tools/specs.js +327 -0
- package/server/dist/mcp/tools/types.js +137 -0
- package/server/dist/mcp/tools/watch.js +93 -0
- package/server/dist/mobile/mobile-event-bus.js +2 -0
- package/server/dist/project-router-tickets.js +2 -2
- package/server/dist/rails-router.js +5 -0
- package/client/dist/assets/AgentsPage-jcPUtPhP.js +0 -86
- package/client/dist/assets/CodePage-BcbZT88K.js +0 -2
- package/client/dist/assets/JobDetailPage-D5Cxw0md.js +0 -16
- package/client/dist/assets/JobsPage-3v_iX83r.js +0 -1
- package/client/dist/assets/LoopBuilderPage-4s_adZy5.js +0 -7
- package/client/dist/assets/LoopsPage-DA94-o0j.js +0 -1
- package/client/dist/assets/index-meTWPBzY.js +0 -144
- package/client/dist/assets/setup-BOmQ4vh2.js +0 -1
- package/client/dist/assets/setup-BwO2-kuK.js +0 -1
- package/client/dist/assets/setup-CUTNWz-0.js +0 -1
- package/client/dist/assets/setup-Cf3hjChU.js +0 -1
- package/client/dist/assets/setup-CsuXrbJ2.js +0 -1
- package/client/dist/assets/setup-Df4cJOqu.js +0 -1
- package/client/dist/assets/setup-Do0fWx6W.js +0 -1
- package/client/dist/assets/setup-GlkDhBlJ.js +0 -1
- /package/client/dist/assets/{abap-CK68cr9-.js → abap-BFJA_Joo.js} +0 -0
- /package/client/dist/assets/{activity-D4dhNEwO.js → activity-BXBMi28x.js} +0 -0
- /package/client/dist/assets/{activity-usnNa7FX.js → activity-By6I70Ws.js} +0 -0
- /package/client/dist/assets/{activity-BbNkUmW4.js → activity-CfedQaof.js} +0 -0
- /package/client/dist/assets/{activity-B2MvEH54.js → activity-D62CDlQz.js} +0 -0
- /package/client/dist/assets/{activity-DHjfmAyT.js → activity-DAFuSwnt.js} +0 -0
- /package/client/dist/assets/{activity-C7VdmeGb.js → activity-Did0FnrH.js} +0 -0
- /package/client/dist/assets/{activity-Di2f50_Z.js → activity-oLgwaVyt.js} +0 -0
- /package/client/dist/assets/{addon-image-biWX2UUm.js → addon-image-bPlduuUZ.js} +0 -0
- /package/client/dist/assets/{addon-ligatures-D4EiDb5r.js → addon-ligatures-BexpF7xb.js} +0 -0
- /package/client/dist/assets/{addon-webgl-CDCIZQ03.js → addon-webgl-Zy4IOieH.js} +0 -0
- /package/client/dist/assets/{addspec-BxMn39wn.js → addspec-B2fTENuq.js} +0 -0
- /package/client/dist/assets/{addspec-DAveBiCE.js → addspec-BmggyuCD.js} +0 -0
- /package/client/dist/assets/{addspec-1HEtmLv1.js → addspec-CFsLeQKV.js} +0 -0
- /package/client/dist/assets/{addspec-DfuR6EAl.js → addspec-CkHI5c5H.js} +0 -0
- /package/client/dist/assets/{addspec-CO1xv6BL.js → addspec-Cvm06hGe.js} +0 -0
- /package/client/dist/assets/{addspec-IozoiiGs.js → addspec-DwoLTocx.js} +0 -0
- /package/client/dist/assets/{addspec-CNyAUGz_.js → addspec-gthH-H1g.js} +0 -0
- /package/client/dist/assets/{agentstudio-Q0Sq_jTg.js → agentstudio-BtASTJl7.js} +0 -0
- /package/client/dist/assets/{agentstudio-9NbsoGXk.js → agentstudio-CIe9C892.js} +0 -0
- /package/client/dist/assets/{agentstudio-B-jQgYV-.js → agentstudio-CYGk-jjo.js} +0 -0
- /package/client/dist/assets/{agentstudio-Bwa72b1S.js → agentstudio-CwiS3BTp.js} +0 -0
- /package/client/dist/assets/{agentstudio-B1y0JWY-.js → agentstudio-D0bQBWpI.js} +0 -0
- /package/client/dist/assets/{agentstudio-DCyDW8sy.js → agentstudio-Dq-bZrI3.js} +0 -0
- /package/client/dist/assets/{agentstudio-1E-bag77.js → agentstudio-n0pmwz2I.js} +0 -0
- /package/client/dist/assets/{aiedit-qTGI_PGs.js → aiedit-1Z-UYKFb.js} +0 -0
- /package/client/dist/assets/{aiedit-CGxeml8D.js → aiedit-BAJM9Cch.js} +0 -0
- /package/client/dist/assets/{aiedit-CfBNanHE.js → aiedit-CYnsp1Iv.js} +0 -0
- /package/client/dist/assets/{aiedit-CvSspQmE.js → aiedit-CZBZAyeK.js} +0 -0
- /package/client/dist/assets/{aiedit-kAU7KADg.js → aiedit-CunYxmW0.js} +0 -0
- /package/client/dist/assets/{aiedit-DUpe7NxI.js → aiedit-s1T1EMK8.js} +0 -0
- /package/client/dist/assets/{aiedit-DSLtsYIu.js → aiedit-uIDh2tlc.js} +0 -0
- /package/client/dist/assets/{analytics-DBGO8B6-.js → analytics-B95FiMuD.js} +0 -0
- /package/client/dist/assets/{analytics-CgEkAUiP.js → analytics-DPIkvuTS.js} +0 -0
- /package/client/dist/assets/{analytics-DDqve9Ma.js → analytics-DPmCEzvJ.js} +0 -0
- /package/client/dist/assets/{analytics-D1ElChhs.js → analytics-DRexho82.js} +0 -0
- /package/client/dist/assets/{analytics-DEsEAJZL.js → analytics-DVFNjjAv.js} +0 -0
- /package/client/dist/assets/{analytics-BDlELxc7.js → analytics-DZKGAUdm.js} +0 -0
- /package/client/dist/assets/{analytics-CQqu1yE5.js → analytics-g9ASAR_r.js} +0 -0
- /package/client/dist/assets/{apex-BHHyAVNb.js → apex-C1f-z6NO.js} +0 -0
- /package/client/dist/assets/{atom-one-dark-DrWoEEbZ.js → atom-one-dark-CT4DolNR.js} +0 -0
- /package/client/dist/assets/{attachments-3CuBBRFu.js → attachments--jr2QLGv.js} +0 -0
- /package/client/dist/assets/{attachments-pW3zm9nu.js → attachments-BgqfeoOx.js} +0 -0
- /package/client/dist/assets/{attachments-BNgH6Tcu.js → attachments-Bj3HHTXV.js} +0 -0
- /package/client/dist/assets/{attachments-D5fVsMgb.js → attachments-C3skjFyH.js} +0 -0
- /package/client/dist/assets/{attachments-UJtEq892.js → attachments-CnPS8YWu.js} +0 -0
- /package/client/dist/assets/{attachments-CppM7de8.js → attachments-DejzxE0C.js} +0 -0
- /package/client/dist/assets/{attachments-BLqqPGyy.js → attachments-FFb2xHc_.js} +0 -0
- /package/client/dist/assets/{azcli-C56lfIEP.js → azcli-Dr0WBJTP.js} +0 -0
- /package/client/dist/assets/{bat-BLamSaf-.js → bat-BKG3k49W.js} +0 -0
- /package/client/dist/assets/{bicep-lrOcEItS.js → bicep-DXs2wTcS.js} +0 -0
- /package/client/dist/assets/{browser-C91MQBxx.js → browser-BS9t1qMc.js} +0 -0
- /package/client/dist/assets/{browser-B7i7Ysc8.js → browser-BpMo-amf.js} +0 -0
- /package/client/dist/assets/{browser-DwE289IS.js → browser-CsC3NOZ-.js} +0 -0
- /package/client/dist/assets/{browser-C__Vz7KR.js → browser-DS6xnywt.js} +0 -0
- /package/client/dist/assets/{browser-DhwTc0ry.js → browser-Dm6F9bqQ.js} +0 -0
- /package/client/dist/assets/{browser-EFmPpkvO.js → browser-Dm_Of27q.js} +0 -0
- /package/client/dist/assets/{browser-BVQgd1_1.js → browser-U3iouSXn.js} +0 -0
- /package/client/dist/assets/{cameligo-BK_p0bgU.js → cameligo-Detknuxp.js} +0 -0
- /package/client/dist/assets/{chat-PVakYp92.js → chat-B2tWTbZL.js} +0 -0
- /package/client/dist/assets/{chat-BnJPyeUK.js → chat-CXOJHDUZ.js} +0 -0
- /package/client/dist/assets/{chat-CE8bx6Rz.js → chat-Cy78lw1n.js} +0 -0
- /package/client/dist/assets/{chat-UMl6H55K.js → chat-D78VMi-v.js} +0 -0
- /package/client/dist/assets/{chat-BXVx6rb5.js → chat-De1RgOXU.js} +0 -0
- /package/client/dist/assets/{chat-C5nJF6L0.js → chat-DyTbH-0F.js} +0 -0
- /package/client/dist/assets/{chat-9RcD5Lcn.js → chat-sqZyn0C4.js} +0 -0
- /package/client/dist/assets/{clojure-BQ7JOykv.js → clojure-CCu4UNKg.js} +0 -0
- /package/client/dist/assets/{clsx-CjLeUJfG.js → clsx-pVRQNNR0.js} +0 -0
- /package/client/dist/assets/{code-u9Sg9ohM.js → code-6LokPmIE.js} +0 -0
- /package/client/dist/assets/{code-OoLqhjoI.js → code-CLTuhuvM.js} +0 -0
- /package/client/dist/assets/{code-Lg4awa35.js → code-CwGlPuwb.js} +0 -0
- /package/client/dist/assets/{code-BPMR8_Vp.js → code-DFCDV5e1.js} +0 -0
- /package/client/dist/assets/{code-eh4vO2-_.js → code-Djq48g0s.js} +0 -0
- /package/client/dist/assets/{code-aCUqkVN_.js → code-Do27r8WG.js} +0 -0
- /package/client/dist/assets/{code-BsOBm1Fb.js → code-Drc7f197.js} +0 -0
- /package/client/dist/assets/{coffee-BD3DiL5s.js → coffee-Bam7Y9IR.js} +0 -0
- /package/client/dist/assets/{commands-B9XEyMj-.js → commands-CxReviyc.js} +0 -0
- /package/client/dist/assets/{commands-Ic1DJDxm.js → commands-CzP1qQyD.js} +0 -0
- /package/client/dist/assets/{commands-BdI0CtOL.js → commands-DGP46UxM.js} +0 -0
- /package/client/dist/assets/{commands-DSX3yJyr.js → commands-DQvoMqa7.js} +0 -0
- /package/client/dist/assets/{commands-yNn4IA4C.js → commands-DeKaz04Q.js} +0 -0
- /package/client/dist/assets/{commands-BI1Ta-E7.js → commands-DwYhwQsF.js} +0 -0
- /package/client/dist/assets/{commands-C2ewlyXo.js → commands-lm6Vlght.js} +0 -0
- /package/client/dist/assets/{common-CzCQ0rpW.js → common-B0crwfK0.js} +0 -0
- /package/client/dist/assets/{common-CNoRVpnA.js → common-BlqmiTj4.js} +0 -0
- /package/client/dist/assets/{common-CcHFVATT.js → common-CIUPtPdu.js} +0 -0
- /package/client/dist/assets/{common-LDwESeCX.js → common-C_-0fZsp.js} +0 -0
- /package/client/dist/assets/{common-C9VyfY7g.js → common-DH9A-wTo.js} +0 -0
- /package/client/dist/assets/{common-Dmpyh323.js → common-DJM41wFl.js} +0 -0
- /package/client/dist/assets/{common-ULlk84G4.js → common-SyTaRc_K.js} +0 -0
- /package/client/dist/assets/{cpp-splnCwCh.js → cpp-BhOkAe-9.js} +0 -0
- /package/client/dist/assets/{csharp-BCpsl-o3.js → csharp-BgHArJNx.js} +0 -0
- /package/client/dist/assets/{csp-tkCWrZmb.js → csp-Xzh56_3-.js} +0 -0
- /package/client/dist/assets/{css-B_xdnReq.js → css-CD7-RYRS.js} +0 -0
- /package/client/dist/assets/{cypher-OL1zq-y3.js → cypher-l2xAdF3X.js} +0 -0
- /package/client/dist/assets/{dart-CtTlOkrm.js → dart-CDOWeyzt.js} +0 -0
- /package/client/dist/assets/{dashboard-C-dXxut-.js → dashboard-B1rs1wki.js} +0 -0
- /package/client/dist/assets/{dashboard-CUjyhohl.js → dashboard-D9QPCmv3.js} +0 -0
- /package/client/dist/assets/{dashboard-C52PElPV.js → dashboard-Dxgb47lO.js} +0 -0
- /package/client/dist/assets/{dashboard-03EpOoMM.js → dashboard-TuElRCZX.js} +0 -0
- /package/client/dist/assets/{dashboard-Ch6H-457.js → dashboard-UWXs9_wy.js} +0 -0
- /package/client/dist/assets/{dashboard-zH9quXks.js → dashboard-ajT8usAi.js} +0 -0
- /package/client/dist/assets/{dashboard-C2owt3eN.js → dashboard-zwN2xswo.js} +0 -0
- /package/client/dist/assets/{dockerfile-DcQ5CNiW.js → dockerfile-Dv8atx7v.js} +0 -0
- /package/client/dist/assets/{ecl-DZpiDRt6.js → ecl-DIYTlbD_.js} +0 -0
- /package/client/dist/assets/{editor.api2-Cwla1nvV.js → editor.api2-CrNrMVfe.js} +0 -0
- /package/client/dist/assets/{elixir-Dzj2lqtK.js → elixir-CR7PLJja.js} +0 -0
- /package/client/dist/assets/{explore-4SD1DxR0.js → explore-BYFCCka4.js} +0 -0
- /package/client/dist/assets/{explore-xQoaak6e.js → explore-CE0wSNV1.js} +0 -0
- /package/client/dist/assets/{explore-CZt4Ncpa.js → explore-CSDcRUNj.js} +0 -0
- /package/client/dist/assets/{explore-DQ0XEjed.js → explore-CvM08PpD.js} +0 -0
- /package/client/dist/assets/{explore-CCcPXrHg.js → explore-Jkw93usp.js} +0 -0
- /package/client/dist/assets/{explore-BxR9q8Hq.js → explore-kK239uoZ.js} +0 -0
- /package/client/dist/assets/{explore-B2n8r9Ye.js → explore-mLI5eXks.js} +0 -0
- /package/client/dist/assets/{flow9-CFENCHIZ.js → flow9-DCK5-6NG.js} +0 -0
- /package/client/dist/assets/{format-command-CMsBTOPN.js → format-command-rVyGPxWE.js} +0 -0
- /package/client/dist/assets/{fsharp-D-4bv7FB.js → fsharp-DJ1xiUVU.js} +0 -0
- /package/client/dist/assets/{go-djxbTJq7.js → go-BEvbHcbb.js} +0 -0
- /package/client/dist/assets/{graphql-DHIFrkZ6.js → graphql-DN1ktXnz.js} +0 -0
- /package/client/dist/assets/{hcl-CXVoowNb.js → hcl-DJaIjIEr.js} +0 -0
- /package/client/dist/assets/{ini-Crzc5kly.js → ini-PlKfLEnb.js} +0 -0
- /package/client/dist/assets/{integrations-A9f08E0o.js → integrations-B8hT0Qf_.js} +0 -0
- /package/client/dist/assets/{integrations-DwSVdCpf.js → integrations-Bdk5dMyv.js} +0 -0
- /package/client/dist/assets/{integrations-B46ac3sf.js → integrations-CYqz7YFS.js} +0 -0
- /package/client/dist/assets/{integrations-9qxUNhPD.js → integrations-Cj_BsXxJ.js} +0 -0
- /package/client/dist/assets/{integrations-B17KyA-i.js → integrations-D8M8eJH2.js} +0 -0
- /package/client/dist/assets/{integrations-DkBi-Gsh.js → integrations-TJuDBl-9.js} +0 -0
- /package/client/dist/assets/{integrations-C7u7SInG.js → integrations-TwQV3sLw.js} +0 -0
- /package/client/dist/assets/{java-CYLaJrdE.js → java-3PUwMlx-.js} +0 -0
- /package/client/dist/assets/{jira-01H1RfQj.js → jira--rI5PfuE.js} +0 -0
- /package/client/dist/assets/{jira-CBU0200d.js → jira-BoRyckbb.js} +0 -0
- /package/client/dist/assets/{jira-BpnpUFlb.js → jira-C95FN3yk.js} +0 -0
- /package/client/dist/assets/{jira-DzIVvNmy.js → jira-CNUUgTs4.js} +0 -0
- /package/client/dist/assets/{jira-Dht8qNck.js → jira-DHFnvEm8.js} +0 -0
- /package/client/dist/assets/{jira-4Fin3fu7.js → jira-NhkbrDXe.js} +0 -0
- /package/client/dist/assets/{jira-0ldfMByG.js → jira-wiIvGma5.js} +0 -0
- /package/client/dist/assets/{jobs-Oz3UtLQn.js → jobs-Bjp7v9aA.js} +0 -0
- /package/client/dist/assets/{jobs-jEQidSIb.js → jobs-Bjt5PyVv.js} +0 -0
- /package/client/dist/assets/{jobs-BF9raUkL.js → jobs-BmTs4Bn3.js} +0 -0
- /package/client/dist/assets/{jobs-DcCONARH.js → jobs-Cchy-18B.js} +0 -0
- /package/client/dist/assets/{jobs-DM9owF1j.js → jobs-D2QVTPbz.js} +0 -0
- /package/client/dist/assets/{jobs-Bw4hqEro.js → jobs-LK1ImiDH.js} +0 -0
- /package/client/dist/assets/{jobs-ym72SIR9.js → jobs-yPz4Dvi-.js} +0 -0
- /package/client/dist/assets/{julia-BJ7tUU-T.js → julia-DC6LnY15.js} +0 -0
- /package/client/dist/assets/{kotlin-CeWcU3lC.js → kotlin-CoGntbZF.js} +0 -0
- /package/client/dist/assets/{less-CviwWNG4.js → less-DCFflTXL.js} +0 -0
- /package/client/dist/assets/{lexon-Dt28jqDS.js → lexon-BTOivnjP.js} +0 -0
- /package/client/dist/assets/{loops-Fv4Bs-9A.js → loops-BFIcYvpy.js} +0 -0
- /package/client/dist/assets/{loops-D_ZxkD4l.js → loops-BU314kSd.js} +0 -0
- /package/client/dist/assets/{loops-DE-2cmLw.js → loops-C3OaXou9.js} +0 -0
- /package/client/dist/assets/{loops-mcoeFMat.js → loops-Cv6Aeszq.js} +0 -0
- /package/client/dist/assets/{loops-B_w7STrw.js → loops-IQe23ef6.js} +0 -0
- /package/client/dist/assets/{loops-BYNUJVyD.js → loops-LqZ4MC6J.js} +0 -0
- /package/client/dist/assets/{loops-B_gS4Pqu.js → loops-SbkOdciY.js} +0 -0
- /package/client/dist/assets/{lua-B6AOaJSi.js → lua-ClKCZMmP.js} +0 -0
- /package/client/dist/assets/{m3-VnMkHJcy.js → m3-25hKFWMb.js} +0 -0
- /package/client/dist/assets/{markdown-8vKNdvIM.js → markdown-BGAj9q0K.js} +0 -0
- /package/client/dist/assets/{mips-DoEcuzan.js → mips-BrVgY2Pf.js} +0 -0
- /package/client/dist/assets/{msdax-CNzvHyEp.js → msdax-7EhcRsm8.js} +0 -0
- /package/client/dist/assets/{mysql-Buk3vUtL.js → mysql-B5_atCBH.js} +0 -0
- /package/client/dist/assets/{nav-_0vAgZ_k.js → nav-B2Cv1vjz.js} +0 -0
- /package/client/dist/assets/{nav-Cw3_40fA.js → nav-Br2AivTc.js} +0 -0
- /package/client/dist/assets/{nav-DNDJ3H-_.js → nav-CFofO7QO.js} +0 -0
- /package/client/dist/assets/{nav-CCz4krPP.js → nav-CyHKTZxn.js} +0 -0
- /package/client/dist/assets/{nav-B5W9IMnf.js → nav-DqyLGJmF.js} +0 -0
- /package/client/dist/assets/{nav-CrG_mYRr.js → nav-_-siCrRv.js} +0 -0
- /package/client/dist/assets/{nav-DbAbG5_k.js → nav-rO4Nqnh-.js} +0 -0
- /package/client/dist/assets/{nav-DVp1nEsi.js → nav-zyBhf29M.js} +0 -0
- /package/client/dist/assets/{objective-c-DfOmda-5.js → objective-c-CLUvUl9T.js} +0 -0
- /package/client/dist/assets/{pascal-DoAA2yjy.js → pascal-BnU1HU42.js} +0 -0
- /package/client/dist/assets/{pascaligo-C65Vn1fu.js → pascaligo-dsKvjFzi.js} +0 -0
- /package/client/dist/assets/{perl-INlfQ_ro.js → perl-CZne1GYG.js} +0 -0
- /package/client/dist/assets/{pgsql-DtwRzglx.js → pgsql-CMa5W_iF.js} +0 -0
- /package/client/dist/assets/{php-DfHqVNet.js → php-Cu4mS35J.js} +0 -0
- /package/client/dist/assets/{pla-D3dNVRYk.js → pla-B7COwnnm.js} +0 -0
- /package/client/dist/assets/{postiats-UKXD1rM4.js → postiats-GLeNtnBQ.js} +0 -0
- /package/client/dist/assets/{powerquery-zE3E3O7s.js → powerquery-BqPf3R1W.js} +0 -0
- /package/client/dist/assets/{powershell-Df9b3zQl.js → powershell-D9btb5su.js} +0 -0
- /package/client/dist/assets/{protobuf-BF0nUVrW.js → protobuf-6GHSuaNm.js} +0 -0
- /package/client/dist/assets/{pug-Bj6Yikv_.js → pug-ct6rYKQd.js} +0 -0
- /package/client/dist/assets/{qsharp-BI4h-raZ.js → qsharp-CIrAQNPy.js} +0 -0
- /package/client/dist/assets/{r-DhpnmaXx.js → r-Bk-a0sON.js} +0 -0
- /package/client/dist/assets/{redis-DwYYPr4d.js → redis-C9yfaWYh.js} +0 -0
- /package/client/dist/assets/{redshift-D2f3Bc-4.js → redshift-DaOT1jRU.js} +0 -0
- /package/client/dist/assets/{restructuredtext-Dkiif6fe.js → restructuredtext-C1t9LQfS.js} +0 -0
- /package/client/dist/assets/{ruby-C6hLmHy0.js → ruby-COiPZJjc.js} +0 -0
- /package/client/dist/assets/{rust-X8WAGtxB.js → rust--gZOJZzj.js} +0 -0
- /package/client/dist/assets/{sb-4M2la0Kr.js → sb-NHf3iazF.js} +0 -0
- /package/client/dist/assets/{scala-CaCXz58E.js → scala-D1nO1TBd.js} +0 -0
- /package/client/dist/assets/{scheme-CMdjpmP7.js → scheme-Bref42s5.js} +0 -0
- /package/client/dist/assets/{scss-QopmvPyp.js → scss-BS9CCLr3.js} +0 -0
- /package/client/dist/assets/{settings-DyBxunCh.js → settings-BN4dufu6.js} +0 -0
- /package/client/dist/assets/{settings-BN7OusER.js → settings-Bap_q3rt.js} +0 -0
- /package/client/dist/assets/{settings-Bi5FFkIw.js → settings-BlYjWMy5.js} +0 -0
- /package/client/dist/assets/{settings-iQe5Dim-.js → settings-C7t_uUz4.js} +0 -0
- /package/client/dist/assets/{settings-fk-WlnUt.js → settings-CIFrUsKt.js} +0 -0
- /package/client/dist/assets/{settings-BA1V6KdK.js → settings-Cbj9VWmI.js} +0 -0
- /package/client/dist/assets/{settings-CRJxyTNN.js → settings-D7saHMU8.js} +0 -0
- /package/client/dist/assets/{settings-0m2iuTzd.js → settings-DrRjM5e8.js} +0 -0
- /package/client/dist/assets/{shell-CDBchS00.js → shell-DT3joMBB.js} +0 -0
- /package/client/dist/assets/{solidity-DvBfi5mC.js → solidity-o2Fm9dvg.js} +0 -0
- /package/client/dist/assets/{sophia-HSnXR17u.js → sophia-Dj1N6Xqb.js} +0 -0
- /package/client/dist/assets/{sparql-DhG_ppup.js → sparql-D2fjpTWm.js} +0 -0
- /package/client/dist/assets/{specs-BvsFNKTs.js → specs-C-WXGBqy.js} +0 -0
- /package/client/dist/assets/{specs-D4ORogcT.js → specs-C57koADY.js} +0 -0
- /package/client/dist/assets/{specs-CnH7dOiF.js → specs-CCXV-eSF.js} +0 -0
- /package/client/dist/assets/{specs-CUE3xSFM.js → specs-CYA5Bv7Q.js} +0 -0
- /package/client/dist/assets/{specs-C2wW5bq8.js → specs-DFCFg51O.js} +0 -0
- /package/client/dist/assets/{specs-C_8j4ELZ.js → specs-Dslng_yW.js} +0 -0
- /package/client/dist/assets/{specs-D6Y76Bgi.js → specs-DwRPiJTc.js} +0 -0
- /package/client/dist/assets/{specs-Bku8SypT.js → specs-DzlROLy7.js} +0 -0
- /package/client/dist/assets/{sql-DHeIS-tf.js → sql-BArhsPUz.js} +0 -0
- /package/client/dist/assets/{st-CMWchiRU.js → st-Bd2-tALj.js} +0 -0
- /package/client/dist/assets/{swift-CDXvBefj.js → swift-DvEpXBKj.js} +0 -0
- /package/client/dist/assets/{systemverilog-0RTukYUE.js → systemverilog-mHEbsnVH.js} +0 -0
- /package/client/dist/assets/{tcl-CbBF_Ttl.js → tcl-DePutp1e.js} +0 -0
- /package/client/dist/assets/{terminal-CB33IsO1.js → terminal-2S-Wsk6L.js} +0 -0
- /package/client/dist/assets/{terminal-Cz_Puz0q.js → terminal-BRQMrU74.js} +0 -0
- /package/client/dist/assets/{terminal-DFEH9XTQ.js → terminal-BTiemy98.js} +0 -0
- /package/client/dist/assets/{terminal-BTanJIt_.js → terminal-CjQeWfpp.js} +0 -0
- /package/client/dist/assets/{terminal-CY_sPfdD.js → terminal-CmP5riTU.js} +0 -0
- /package/client/dist/assets/{terminal-BLh00ouM.js → terminal-CnObBCAh.js} +0 -0
- /package/client/dist/assets/{terminal-CR6t-Oey.js → terminal-DcPKgKMN.js} +0 -0
- /package/client/dist/assets/{terminal-Cj4HzP3p.js → terminal-XciDq6VV.js} +0 -0
- /package/client/dist/assets/{tickets-OrJwUxJR.js → tickets-7kl_0jgM.js} +0 -0
- /package/client/dist/assets/{tickets-68LH1xp6.js → tickets-BSyaZI8d.js} +0 -0
- /package/client/dist/assets/{tickets-blQnNA4N.js → tickets-CcTcvwKS.js} +0 -0
- /package/client/dist/assets/{tickets-D6f3GSYn.js → tickets-D75vyZJj.js} +0 -0
- /package/client/dist/assets/{tickets-PtHNxQo5.js → tickets-D79ZT2SZ.js} +0 -0
- /package/client/dist/assets/{tickets-DBQXMYYA.js → tickets-DbcMWq5L.js} +0 -0
- /package/client/dist/assets/{tickets-jxsTrApR.js → tickets-ZA_2iPd1.js} +0 -0
- /package/client/dist/assets/{tickets-REr-EQx9.js → tickets-mxd23fWX.js} +0 -0
- /package/client/dist/assets/{twig-DCcGd8FE.js → twig-DQ2zIX0b.js} +0 -0
- /package/client/dist/assets/{typespec-HTphp-Uh.js → typespec-DPoaiGTJ.js} +0 -0
- /package/client/dist/assets/{vb-Cb_yUcVd.js → vb-Jo98wdMr.js} +0 -0
- /package/client/dist/assets/{wgsl-DrKU3YXj.js → wgsl-DZc4h_eS.js} +0 -0
package/docs/mcp.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# The Specrails MCP server
|
|
2
|
+
|
|
3
|
+
Specrails Desktop can expose itself to **any MCP client** — Claude Desktop,
|
|
4
|
+
Claude Code, Cursor, or your own agent — as a local
|
|
5
|
+
[Model Context Protocol](https://modelcontextprotocol.io) server. Turn it on
|
|
6
|
+
and an external LLM can drive the whole dashboard: list your projects, read and
|
|
7
|
+
create specs, launch the AI pipeline, watch jobs settle, inspect analytics, and
|
|
8
|
+
more — through ~18 well-described tools instead of clicking around the UI.
|
|
9
|
+
|
|
10
|
+
> **Just want to get going?** Open **Settings ▸ MCP**, flip **Enable MCP** on,
|
|
11
|
+
> click **Copy client config**, and paste it into your MCP client. By default
|
|
12
|
+
> only **read** actions are allowed; turn on the **Write** / **AI-spawn** /
|
|
13
|
+
> **Destructive** tiers when you want the agent to actually change things or
|
|
14
|
+
> spend money. Everything below is detail for when you need it.
|
|
15
|
+
|
|
16
|
+
This is the **app talking to an outside agent** — the opposite direction from
|
|
17
|
+
the [Serena plugin](running-pipelines.md#plugins) or
|
|
18
|
+
`codex mcp add`, where Specrails *consumes* an MCP server. Here Specrails *is*
|
|
19
|
+
the server.
|
|
20
|
+
|
|
21
|
+
The MCP server is **off by default** and entirely local: it listens only on
|
|
22
|
+
loopback (`127.0.0.1`), is authenticated by a token separate from the app's
|
|
23
|
+
master token, and serves nothing until you explicitly enable it in Settings.
|
|
24
|
+
|
|
25
|
+
## What the MCP exposes
|
|
26
|
+
|
|
27
|
+
When enabled, the server registers a compact catalog of **domain-facade tools**
|
|
28
|
+
plus a few **meta tools**, a set of read-only **resources**, and a self-contained
|
|
29
|
+
**guide** an LLM can read to learn the platform with no prior knowledge.
|
|
30
|
+
|
|
31
|
+
### Tools (~18)
|
|
32
|
+
|
|
33
|
+
Each domain is a single tool with an `action` enum, rather than dozens of
|
|
34
|
+
narrow tools — so the catalog stays small and an agent discovers actions by
|
|
35
|
+
reading one description. The tools are:
|
|
36
|
+
|
|
37
|
+
| Tool | What it covers |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `specrails_projects` | List / resolve projects; unregister (destructive) |
|
|
40
|
+
| `specrails_specs` | The spec/ticket backlog: list, get, create, update, delete, drafts, AI generate, AI-edit, Contract Refine, SMASH, per-ticket spend |
|
|
41
|
+
| `specrails_rails` | Assign tickets to a rail, configure it, and launch the pipeline |
|
|
42
|
+
| `specrails_jobs` | Inspect, stream, and stop pipeline jobs |
|
|
43
|
+
| `specrails_chat` | Explore / sidebar chat conversations and turns |
|
|
44
|
+
| `specrails_agents` | Agent profiles and the agents catalog (Claude-only features) |
|
|
45
|
+
| `specrails_plugins` | The per-project plugin marketplace (install / verify / uninstall) |
|
|
46
|
+
| `specrails_jira` | The Jira integration (connect, sync, outbox) |
|
|
47
|
+
| `specrails_loops` | Saved loop workflows |
|
|
48
|
+
| `specrails_code` | The read-only code explorer (tree, file, summaries, provenance) |
|
|
49
|
+
| `specrails_setup` | Add-project setup wizard surface |
|
|
50
|
+
| `specrails_analytics` | Per-project spending analytics + budget |
|
|
51
|
+
| `specrails_settings` | App-level settings |
|
|
52
|
+
| `specrails_watch` | Await the real result of an async (cost-incurring) action |
|
|
53
|
+
| `specrails_guide` | Returns the platform guide — read this first |
|
|
54
|
+
| `specrails_search` | Find the right tool/action for an intent |
|
|
55
|
+
| `specrails_describe` | Full description + input schema for a named tool |
|
|
56
|
+
| `specrails_select_project` | Set an active project so later calls can omit `projectId` |
|
|
57
|
+
|
|
58
|
+
Almost every domain tool is **project-scoped** and takes a `projectId`. Calling
|
|
59
|
+
`specrails_select_project` first lets later calls omit it.
|
|
60
|
+
|
|
61
|
+
### Resources
|
|
62
|
+
|
|
63
|
+
Read-only state is also exposed as MCP **resources** so a client can fetch it
|
|
64
|
+
without a tool call:
|
|
65
|
+
|
|
66
|
+
- `specrails://guide` — the platform guide (concepts, workflow, invariants).
|
|
67
|
+
- `specrails://projects` — all registered projects.
|
|
68
|
+
- `specrails://projects/{projectId}` — a single project.
|
|
69
|
+
|
|
70
|
+
### Async results — `specrails_watch`
|
|
71
|
+
|
|
72
|
+
Cost-incurring and streaming actions (launch a rail, generate a spec, send a
|
|
73
|
+
chat turn, AI-edit, Contract Refine, SMASH) **return immediately** with a
|
|
74
|
+
reference (a `jobId` / `requestId`) and emit their **real** result over the
|
|
75
|
+
app's WebSocket bus. An agent gets the actual outcome by calling
|
|
76
|
+
`specrails_watch` with that reference; it waits for the operation to settle and
|
|
77
|
+
returns the final result. Don't assume success from the acceptance alone — this
|
|
78
|
+
is spelled out in `specrails_guide`.
|
|
79
|
+
|
|
80
|
+
## The four permission tiers
|
|
81
|
+
|
|
82
|
+
Every tool declares one tier. The server refuses any tool whose tier is not
|
|
83
|
+
enabled, and the refusal **names the tier the user must turn on** so the agent
|
|
84
|
+
can relay it rather than retrying blindly. The tiers are **opt-in and
|
|
85
|
+
cumulative** — read is always on, the rest are off until you enable them in
|
|
86
|
+
**Settings ▸ MCP**:
|
|
87
|
+
|
|
88
|
+
| Tier | Default | What it allows |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| **Read** | Always on | Queries + resources (list specs, read analytics, inspect jobs) |
|
|
91
|
+
| **Write** | Off | Mutating but non-destructive, non-spawn: create/edit specs, change settings, configure a rail |
|
|
92
|
+
| **AI-spawn** | Off | Actions that spawn an AI CLI and **cost money**: launch a rail, generate a spec, send a chat turn |
|
|
93
|
+
| **Destructive** | Off | Delete data, kill processes, or mutate an external system (e.g. unregister a project, `smash_undo`, Jira writes) |
|
|
94
|
+
|
|
95
|
+
The split is deliberate: a client you only half-trust can be left read-only;
|
|
96
|
+
**destructive and cost-incurring actions are opt-in** and never happen by
|
|
97
|
+
accident. A tool's tier is dynamic per action — e.g. `specrails_specs(list)` is
|
|
98
|
+
read while `specrails_specs(delete)` is destructive — so the same tool exposes
|
|
99
|
+
different actions at different trust levels.
|
|
100
|
+
|
|
101
|
+
## Enabling it
|
|
102
|
+
|
|
103
|
+
1. Open the app and go to **Settings ▸ MCP**.
|
|
104
|
+
2. Toggle **Enable MCP**. This boots the embedded transport immediately — no
|
|
105
|
+
app restart. (Behind the scenes it persists `mcp_enabled` and starts serving
|
|
106
|
+
`/api/mcp`.)
|
|
107
|
+
3. Enable the permission tiers you want (**Write** / **AI-spawn** /
|
|
108
|
+
**Destructive**). Leave them off to keep the agent read-only.
|
|
109
|
+
4. Click **Copy client config** to grab a ready-to-paste configuration, or
|
|
110
|
+
**Copy token** if your client needs the raw token (for the direct-HTTP path
|
|
111
|
+
below).
|
|
112
|
+
|
|
113
|
+
Toggling the enable switch off again tears down all open MCP sessions
|
|
114
|
+
immediately.
|
|
115
|
+
|
|
116
|
+
## Connecting a client
|
|
117
|
+
|
|
118
|
+
There are two ways to connect, and **the stdio bridge is the recommended,
|
|
119
|
+
universal one.**
|
|
120
|
+
|
|
121
|
+
### Option A — the stdio bridge `specrails-mcp` (recommended)
|
|
122
|
+
|
|
123
|
+
Most MCP clients (Claude Desktop, Claude Code, Cursor) speak MCP over **stdio**:
|
|
124
|
+
they launch a command and talk to it on stdin/stdout. Specrails ships a tiny
|
|
125
|
+
relay, **`specrails-mcp`**, bundled with the app. It transparently forwards MCP
|
|
126
|
+
stdio from your client to the embedded HTTP server on loopback — and it reads
|
|
127
|
+
the scoped token from `~/.specrails/mcp.token` **locally**, so the token never
|
|
128
|
+
appears in your client's config file.
|
|
129
|
+
|
|
130
|
+
A typical client config looks like:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"mcpServers": {
|
|
135
|
+
"specrails": {
|
|
136
|
+
"command": "specrails-mcp"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Use the exact command/path from the panel's **Copy client config** button — it
|
|
143
|
+
fills in the path to the bundled bridge for your platform. No token, no URL, no
|
|
144
|
+
port to manage; the bridge handles all of it. If the Specrails app isn't
|
|
145
|
+
running, the bridge replies with a clear *"Specrails app is not running. Start
|
|
146
|
+
the Specrails Desktop app, then retry."* rather than a cryptic connection error.
|
|
147
|
+
|
|
148
|
+
### Option B — the direct HTTP URL (remote-HTTP clients)
|
|
149
|
+
|
|
150
|
+
Clients that speak MCP's **streamable-HTTP** transport directly can skip the
|
|
151
|
+
bridge and connect to:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
http://127.0.0.1:4200/api/mcp
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
with the scoped token as a bearer header:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
Authorization: Bearer <your mcp token>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Get the token from **Settings ▸ MCP ▸ Copy token** (or read
|
|
164
|
+
`~/.specrails/mcp.token`). The endpoint is **loopback-only** — it rejects any
|
|
165
|
+
request that doesn't originate from `127.0.0.1`, so this path is for local
|
|
166
|
+
HTTP-capable clients, not for exposing Specrails on a network. (`4200` is the
|
|
167
|
+
default app port; if you've changed it, the panel's config reflects the real
|
|
168
|
+
one.)
|
|
169
|
+
|
|
170
|
+
## Security model
|
|
171
|
+
|
|
172
|
+
- **A scoped token, not the master token.** The MCP surface is authenticated by
|
|
173
|
+
its own token at `~/.specrails/mcp.token` (mode `0600`), completely separate
|
|
174
|
+
from the app's all-powerful master token (`~/.specrails/desktop.token`). The
|
|
175
|
+
master token grants terminals (shell), arbitrary AI-CLI spawns, and project
|
|
176
|
+
admin; handing that to a third-party MCP client would be a security smell. The
|
|
177
|
+
scoped token only reaches the four tiered surfaces above. You can rotate it
|
|
178
|
+
any time with **Regenerate token** in Settings — the old token stops working
|
|
179
|
+
immediately.
|
|
180
|
+
- **Loopback only.** Both the transport (`/api/mcp`) and the admin control plane
|
|
181
|
+
(`/api/mcp-admin`) require the request to come from `127.0.0.1`. There is no
|
|
182
|
+
network ingress; an MCP client must run on the same machine.
|
|
183
|
+
- **Tiers, not blanket access.** Even with a valid token, an agent can only do
|
|
184
|
+
what the enabled tiers allow (read by default). High-risk actions stay opt-in.
|
|
185
|
+
- **The app must be running.** The MCP server is embedded in the desktop app's
|
|
186
|
+
process — there is no standalone daemon. The Tauri shell keeps it alive even
|
|
187
|
+
when you close the main window: closing the window **minimizes to the system
|
|
188
|
+
tray / menu bar** and leaves the embedded server running, so your MCP client
|
|
189
|
+
stays connected. The server stops only when you choose **Exit** from the tray
|
|
190
|
+
menu (or the OS truly quits the app). If the app isn't running, the bridge
|
|
191
|
+
tells you to start it.
|
|
192
|
+
|
|
193
|
+
## v1 limitations — what is *not* exposed
|
|
194
|
+
|
|
195
|
+
The tool catalog deliberately leaves the highest-risk execution vectors out of
|
|
196
|
+
v1. None of these are reachable over MCP:
|
|
197
|
+
|
|
198
|
+
- **Terminal shell execution.** The built-in terminal panel runs arbitrary
|
|
199
|
+
shell commands; the MCP surface does **not** expose it. An agent cannot run
|
|
200
|
+
shell on your machine through Specrails.
|
|
201
|
+
- **Browser capture.** No browser navigation / screenshot / capture tool.
|
|
202
|
+
- **In-app file write.** The code explorer is **read-only** over MCP — there is
|
|
203
|
+
no source-file overwrite tool (`code_write_file`). An agent can read files and
|
|
204
|
+
AI-generated summaries, but cannot edit your code directly through MCP. (AI
|
|
205
|
+
*rails* still write code — that's the pipeline, gated behind the AI-spawn
|
|
206
|
+
tier — but there is no direct file-overwrite tool.)
|
|
207
|
+
- **The prerequisite installer.** The `uv`/remote-script prerequisite installer
|
|
208
|
+
is not exposed, so an agent cannot trigger remote-script installs.
|
|
209
|
+
- The global Claude marketplace / `~/.claude/settings.json` mutation is also
|
|
210
|
+
out of scope.
|
|
211
|
+
|
|
212
|
+
These boundaries are intentional and may be revisited in a later version.
|
|
213
|
+
|
|
214
|
+
## Troubleshooting
|
|
215
|
+
|
|
216
|
+
**My client can't connect / "app is not running"** — start the Specrails
|
|
217
|
+
Desktop app. The MCP server is embedded in the app process; it isn't a separate
|
|
218
|
+
daemon. If the window is hidden, the app is still running in the tray/menu bar —
|
|
219
|
+
look for the Specrails icon there. Only the tray **Exit** stops the server.
|
|
220
|
+
|
|
221
|
+
**Every tool is refused with a "permission tier" message** — the action needs a
|
|
222
|
+
tier you haven't enabled. Open **Settings ▸ MCP** and turn on **Write** /
|
|
223
|
+
**AI-spawn** / **Destructive** as appropriate. Read-only actions work with no
|
|
224
|
+
extra tiers.
|
|
225
|
+
|
|
226
|
+
**`401 Unauthorized: invalid MCP token`** (direct-HTTP path) — the bearer token
|
|
227
|
+
is wrong or stale. Re-copy it from **Settings ▸ MCP ▸ Copy token**, or
|
|
228
|
+
**Regenerate token** and update your client. The stdio bridge avoids this
|
|
229
|
+
entirely (it reads the token locally).
|
|
230
|
+
|
|
231
|
+
**The agent assumed a rail succeeded but it didn't** — cost-incurring actions
|
|
232
|
+
return a reference, not a result. The agent must call `specrails_watch` with the
|
|
233
|
+
returned `jobId`/`requestId` to get the real outcome. This is documented in
|
|
234
|
+
`specrails_guide`; point the agent at it.
|
|
235
|
+
|
|
236
|
+
**Connection refused on `:4200`** — that's the default app port. If you changed
|
|
237
|
+
the port in Settings, use the URL from **Copy client config**, which reflects
|
|
238
|
+
the real port. The bridge resolves the port automatically.
|
|
239
|
+
|
|
240
|
+
## See also
|
|
241
|
+
|
|
242
|
+
- [Running pipelines](running-pipelines.md) — what rails, jobs, and the
|
|
243
|
+
pipeline an MCP client drives actually do.
|
|
244
|
+
- [Creating specs](creating-specs.md) — specs, drafts, SMASH, and Contract
|
|
245
|
+
Refine, all reachable via `specrails_specs`.
|
|
246
|
+
- [Tracking cost](tracking-cost.md) — the analytics an agent reads via
|
|
247
|
+
`specrails_analytics`.
|
|
248
|
+
- [Customising the app](customizing.md) — other settings, including the budget
|
|
249
|
+
the AI-spawn tier respects.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specrails-desktop",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.17.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,15 +24,16 @@
|
|
|
24
24
|
"dev:server": "tsx watch server/index.ts",
|
|
25
25
|
"dev:client": "cd client && npm run dev",
|
|
26
26
|
"dev:desktop": "tauri dev",
|
|
27
|
-
"build:server": "node -e \"require('fs').rmSync('server/dist',{recursive:true,force:true})\" && tsc --project tsconfig.json",
|
|
27
|
+
"build:server": "node -e \"require('fs').rmSync('server/dist',{recursive:true,force:true})\" && tsc --project tsconfig.build.json --noCheck",
|
|
28
28
|
"build:cli": "tsc --project tsconfig.cli.json",
|
|
29
29
|
"build": "npm run build:server && cd client && npm run build && cd .. && npm run build:cli",
|
|
30
30
|
"build:sidecar": "node scripts/build-sidecar.mjs",
|
|
31
|
-
"build:
|
|
31
|
+
"build:mcp-bridge": "node scripts/build-mcp-bridge.mjs",
|
|
32
|
+
"build:desktop": "npm run build:server && cd client && npm run build && cd .. && npm run build:sidecar && npm run build:mcp-bridge && tauri build",
|
|
32
33
|
"assemble-runtimes": "node scripts/assemble-runtimes-local.mjs",
|
|
33
34
|
"build:desktop:local": "npm run assemble-runtimes && npm run build:desktop",
|
|
34
35
|
"generate-icons": "tauri icon src-tauri/icons/icon.svg",
|
|
35
|
-
"typecheck": "tsc --noEmit && tsc -p tsconfig.cli.json --noEmit && cd client && tsc --noEmit",
|
|
36
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.cli.json --noEmit && tsc -p mcp-bridge/tsconfig.json --noEmit && cd client && tsc --noEmit",
|
|
36
37
|
"check-core-compat": "tsx scripts/check-core-compat.ts",
|
|
37
38
|
"test": "vitest run && tsx scripts/check-core-compat.ts",
|
|
38
39
|
"test:watch": "vitest",
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"ci": "npm run typecheck && npm run check-core-compat && npm run test:coverage && cd client && npm run test:coverage"
|
|
43
44
|
},
|
|
44
45
|
"dependencies": {
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
45
47
|
"@tailwindcss/typography": "^0.5.19",
|
|
46
48
|
"ajv": "^8.17.1",
|
|
47
49
|
"better-sqlite3": "^12.8.0",
|
|
@@ -57,7 +59,8 @@
|
|
|
57
59
|
"selfsigned": "^5.5.0",
|
|
58
60
|
"tree-kill": "^1.2.2",
|
|
59
61
|
"werift": "^0.23.0",
|
|
60
|
-
"ws": "^8.16.0"
|
|
62
|
+
"ws": "^8.16.0",
|
|
63
|
+
"zod": "^3.25.76"
|
|
61
64
|
},
|
|
62
65
|
"devDependencies": {
|
|
63
66
|
"@tauri-apps/cli": "^2.0.0",
|
package/server/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ const terminal_manager_1 = require("./terminal-manager");
|
|
|
23
23
|
const terminal_shell_integration_1 = require("./terminal-shell-integration");
|
|
24
24
|
const feature_flags_1 = require("./feature-flags");
|
|
25
25
|
const mobile_1 = require("./mobile");
|
|
26
|
+
const mcp_1 = require("./mcp");
|
|
26
27
|
const browser_playwright_1 = require("./browser-playwright");
|
|
27
28
|
const telemetry_receiver_1 = require("./telemetry-receiver");
|
|
28
29
|
const telemetry_compactor_1 = require("./telemetry-compactor");
|
|
@@ -150,6 +151,20 @@ app.use(corsMiddleware);
|
|
|
150
151
|
// 12mb limit FIRST — once it sets req._body the global parser below no-ops for
|
|
151
152
|
// these paths. (The actual /otlp router still requires loopback.)
|
|
152
153
|
app.use('/otlp', express_1.default.json({ limit: '12mb' }));
|
|
154
|
+
// ─── MCP streamable-HTTP transport (design D1) ───────────────────────────────
|
|
155
|
+
// Mounted BEFORE the global express.json so it claims `/api/mcp` with a larger
|
|
156
|
+
// body limit (req._body makes the global parser no-op for this path), and
|
|
157
|
+
// BEFORE `app.use('/api', requireAuth)` so it uses the SCOPED mcp token via
|
|
158
|
+
// requireMcpAuth rather than inheriting the all-powerful master token. The
|
|
159
|
+
// dispatcher delegates to the McpServerManager constructed during Super-mode
|
|
160
|
+
// bootstrap (lazily referenced — it exists by request time).
|
|
161
|
+
app.use('/api/mcp', express_1.default.json({ limit: '4mb' }), auth_1.requireLoopback, mcp_1.requireMcpAuth, (req, res) => {
|
|
162
|
+
if (_mcpManager) {
|
|
163
|
+
void _mcpManager.handleHttp(req, res);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
res.status(503).json({ jsonrpc: '2.0', error: { code: -32000, message: 'MCP transport not ready' }, id: null });
|
|
167
|
+
});
|
|
153
168
|
app.use(express_1.default.json({ limit: '1mb' }));
|
|
154
169
|
const server = http_1.default.createServer(app);
|
|
155
170
|
const wsServerOptions = {
|
|
@@ -219,6 +234,7 @@ let _getProjectCount = () => 0;
|
|
|
219
234
|
let _registry = null;
|
|
220
235
|
/** The mobile companion gateway (off by default); torn down on shutdown. */
|
|
221
236
|
let _mobileGateway = null;
|
|
237
|
+
let _mcpManager = null;
|
|
222
238
|
server.on('upgrade', (request, socket, head) => {
|
|
223
239
|
const urlStr = request.url ?? '/';
|
|
224
240
|
const auth = authorizeUpgrade(request);
|
|
@@ -507,6 +523,19 @@ function applyPtyWsRateLimiting(ws) {
|
|
|
507
523
|
if (mobileGateway.isEnabledSetting()) {
|
|
508
524
|
mobileGateway.start().catch((err) => console.error('[mobile-gateway] boot start failed:', err));
|
|
509
525
|
}
|
|
526
|
+
// ─── Embedded MCP server (the /api/mcp transport mounted above) ─────────────
|
|
527
|
+
const mcpManager = new mcp_1.McpServerManager({ registry, broadcast, desktopPort: port, version: PKG_VERSION });
|
|
528
|
+
_mcpManager = mcpManager;
|
|
529
|
+
// Loopback + master-token control plane for the Settings ▸ MCP panel.
|
|
530
|
+
app.use('/api/mcp-admin', auth_1.requireLoopback, (0, mcp_1.createMcpAdminRouter)({
|
|
531
|
+
manager: mcpManager,
|
|
532
|
+
desktopDb: registry.desktopDb,
|
|
533
|
+
desktopPort: port,
|
|
534
|
+
broadcast,
|
|
535
|
+
}));
|
|
536
|
+
if (mcpManager.isEnabledSetting()) {
|
|
537
|
+
mcpManager.start().catch((err) => console.error('[mcp] boot start failed:', err));
|
|
538
|
+
}
|
|
510
539
|
// App-level routes. CRITICAL mount order: the desktop router is mounted at
|
|
511
540
|
// '/api' BEFORE the project router below so its exact routes (e.g.
|
|
512
541
|
// GET /api/projects, DELETE /api/projects/:id) are handled here, while
|
|
@@ -611,6 +640,7 @@ async function shutdown() {
|
|
|
611
640
|
catch { /* ignore */ }
|
|
612
641
|
try {
|
|
613
642
|
await _mobileGateway?.stop();
|
|
643
|
+
await _mcpManager?.stop();
|
|
614
644
|
}
|
|
615
645
|
catch { /* ignore */ }
|
|
616
646
|
try {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The platform guide returned by the `specrails_guide` meta tool and the
|
|
3
|
+
// `specrails://guide` resource. This is what lets an external LLM "understand
|
|
4
|
+
// the platform" without prior knowledge. It is intentionally English (the LLM
|
|
5
|
+
// consumes it); human-facing onboarding docs are localized separately.
|
|
6
|
+
//
|
|
7
|
+
// Keep this BYTE-STABLE where possible (no timestamps / live data) so MCP
|
|
8
|
+
// clients that cache resources stay valid.
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.SPECRAILS_GUIDE = void 0;
|
|
11
|
+
exports.SPECRAILS_GUIDE = `# Specrails Desktop — Agent Guide
|
|
12
|
+
|
|
13
|
+
You are operating Specrails Desktop through its MCP server. Specrails is a local
|
|
14
|
+
dashboard + pipeline runner that manages multiple software projects and drives AI
|
|
15
|
+
coding agents (Claude Code, Codex, Gemini) to implement specs.
|
|
16
|
+
|
|
17
|
+
## Core concepts
|
|
18
|
+
|
|
19
|
+
- **Project**: a registered repository. Almost every tool is project-scoped and
|
|
20
|
+
takes a \`projectId\`. Use \`specrails_select_project\` to set an active project so
|
|
21
|
+
you can omit \`projectId\` on later calls; pass an explicit \`projectId\` to override.
|
|
22
|
+
- **Spec / ticket**: a unit of work in a project's backlog. Statuses: \`draft\`,
|
|
23
|
+
\`todo\`, \`in_progress\`, \`done\`, \`cancelled\`. Priorities: \`critical\`, \`high\`,
|
|
24
|
+
\`medium\`, \`low\`. INVARIANT: priority may be null ONLY when status is \`draft\`.
|
|
25
|
+
- **Rail**: a launch slot that runs the AI pipeline over one or more assigned
|
|
26
|
+
tickets. Modes: \`implement\`, \`batch-implement\`, \`ultracode\`, \`loop\`. Launching a
|
|
27
|
+
rail spawns AI CLI processes that write code, run tests and commit — it costs
|
|
28
|
+
money and runs for minutes.
|
|
29
|
+
- **Job**: one spawned pipeline run. Jobs stream logs/events over the WebSocket
|
|
30
|
+
and settle (completed/failed/canceled). Cost is authoritative for Claude and
|
|
31
|
+
estimated for Codex/Gemini.
|
|
32
|
+
- **Profile**: a per-project agent configuration (which agents, which models,
|
|
33
|
+
routing). Profiles are CLAUDE-ONLY; they are ignored for Codex/Gemini rails.
|
|
34
|
+
- **Provider / engine**: claude, codex, or gemini. A project installs one or
|
|
35
|
+
more. A requested provider must be one of the project's installed providers.
|
|
36
|
+
- **Loop**: a saved, app-driven workflow graph runnable on a rail in \`loop\` mode.
|
|
37
|
+
- **Plugin**: an MCP-based integration installed per project (e.g. serena).
|
|
38
|
+
|
|
39
|
+
## Async results (IMPORTANT)
|
|
40
|
+
|
|
41
|
+
Cost-incurring / streaming actions (launch a rail, generate a spec, send a chat
|
|
42
|
+
turn, refine an agent, SMASH) return immediately with a reference (jobId /
|
|
43
|
+
requestId) and emit their REAL result over the WebSocket bus. To get the actual
|
|
44
|
+
outcome, call \`specrails_watch\` with the returned reference; it waits for the
|
|
45
|
+
operation to settle and returns the final result. Do not assume success from the
|
|
46
|
+
acceptance alone.
|
|
47
|
+
|
|
48
|
+
## Permission tiers
|
|
49
|
+
|
|
50
|
+
The server enforces four opt-in tiers, configured by the user in Settings ▸ MCP:
|
|
51
|
+
- **Read** (always on): queries + resources.
|
|
52
|
+
- **Write**: create/edit specs, settings, rail config.
|
|
53
|
+
- **AI-spawn**: actions that spawn an AI CLI and cost money.
|
|
54
|
+
- **Destructive**: delete data, kill processes, mutate external systems.
|
|
55
|
+
|
|
56
|
+
If a tool is refused, the error names the tier the user must enable. Relay that
|
|
57
|
+
to the user rather than retrying blindly.
|
|
58
|
+
|
|
59
|
+
## Claude-only features
|
|
60
|
+
|
|
61
|
+
Agent profiles, Contract Refine, and SMASH are Claude-only. They are unavailable
|
|
62
|
+
(and rejected) on projects whose selected provider is Codex or Gemini.
|
|
63
|
+
|
|
64
|
+
## Typical workflow
|
|
65
|
+
|
|
66
|
+
1. \`specrails_projects(list)\` → pick a project; \`specrails_select_project\`.
|
|
67
|
+
2. \`specrails_specs(list)\` → inspect the backlog. To ADD a spec from a user
|
|
68
|
+
request, use \`specrails_specs(action:'create')\` — it routes through **Quick
|
|
69
|
+
Add Spec**: the specrails agent generates a structured spec with AI from the
|
|
70
|
+
request, exactly like the app's "Add Spec" button (so MCP-created specs are
|
|
71
|
+
indistinguishable from UI-created ones). It is async — watch the returned ref
|
|
72
|
+
with \`specrails_watch\`. Use \`from_prompt\` only to store a finished spec verbatim.
|
|
73
|
+
3. \`specrails_rails(set_tickets)\` then \`specrails_rails(launch)\` → run the pipeline;
|
|
74
|
+
\`specrails_watch\` the returned jobId.
|
|
75
|
+
4. \`specrails_analytics(spending)\` → review cost.
|
|
76
|
+
|
|
77
|
+
## Resources
|
|
78
|
+
|
|
79
|
+
Read-only state is also exposed as MCP resources: \`specrails://projects\`,
|
|
80
|
+
\`specrails://projects/{id}/tickets\`, \`specrails://projects/{id}/rails\`,
|
|
81
|
+
\`specrails://projects/{id}/analytics\`, and \`specrails://guide\` (this document).
|
|
82
|
+
`;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildToolSpecs = exports.TIER_SETTING_KEY = exports.MCP_ENABLED_KEY = exports.MCP_TIERS = exports.isTierEnabled = exports.isMcpEnabled = exports._resetMcpTokenForTest = exports.loadOrGenerateMcpToken = exports.regenerateMcpToken = exports.getMcpToken = exports.requireMcpAuth = exports.createMcpAdminRouter = exports.McpServerManager = void 0;
|
|
4
|
+
var mcp_server_1 = require("./mcp-server");
|
|
5
|
+
Object.defineProperty(exports, "McpServerManager", { enumerable: true, get: function () { return mcp_server_1.McpServerManager; } });
|
|
6
|
+
var mcp_admin_router_1 = require("./mcp-admin-router");
|
|
7
|
+
Object.defineProperty(exports, "createMcpAdminRouter", { enumerable: true, get: function () { return mcp_admin_router_1.createMcpAdminRouter; } });
|
|
8
|
+
var mcp_token_1 = require("./mcp-token");
|
|
9
|
+
Object.defineProperty(exports, "requireMcpAuth", { enumerable: true, get: function () { return mcp_token_1.requireMcpAuth; } });
|
|
10
|
+
Object.defineProperty(exports, "getMcpToken", { enumerable: true, get: function () { return mcp_token_1.getMcpToken; } });
|
|
11
|
+
Object.defineProperty(exports, "regenerateMcpToken", { enumerable: true, get: function () { return mcp_token_1.regenerateMcpToken; } });
|
|
12
|
+
Object.defineProperty(exports, "loadOrGenerateMcpToken", { enumerable: true, get: function () { return mcp_token_1.loadOrGenerateMcpToken; } });
|
|
13
|
+
Object.defineProperty(exports, "_resetMcpTokenForTest", { enumerable: true, get: function () { return mcp_token_1._resetMcpTokenForTest; } });
|
|
14
|
+
var mcp_tiers_1 = require("./mcp-tiers");
|
|
15
|
+
Object.defineProperty(exports, "isMcpEnabled", { enumerable: true, get: function () { return mcp_tiers_1.isMcpEnabled; } });
|
|
16
|
+
Object.defineProperty(exports, "isTierEnabled", { enumerable: true, get: function () { return mcp_tiers_1.isTierEnabled; } });
|
|
17
|
+
Object.defineProperty(exports, "MCP_TIERS", { enumerable: true, get: function () { return mcp_tiers_1.MCP_TIERS; } });
|
|
18
|
+
Object.defineProperty(exports, "MCP_ENABLED_KEY", { enumerable: true, get: function () { return mcp_tiers_1.MCP_ENABLED_KEY; } });
|
|
19
|
+
Object.defineProperty(exports, "TIER_SETTING_KEY", { enumerable: true, get: function () { return mcp_tiers_1.TIER_SETTING_KEY; } });
|
|
20
|
+
var catalog_1 = require("./tools/catalog");
|
|
21
|
+
Object.defineProperty(exports, "buildToolSpecs", { enumerable: true, get: function () { return catalog_1.buildToolSpecs; } });
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createMcpAdminRouter = createMcpAdminRouter;
|
|
4
|
+
const express_1 = require("express");
|
|
5
|
+
const mcp_token_1 = require("./mcp-token");
|
|
6
|
+
const desktop_db_1 = require("../desktop-db");
|
|
7
|
+
const mcp_tiers_1 = require("./mcp-tiers");
|
|
8
|
+
function tierState(db) {
|
|
9
|
+
return {
|
|
10
|
+
write: (0, mcp_tiers_1.isTierEnabled)(db, 'write'),
|
|
11
|
+
aiSpawn: (0, mcp_tiers_1.isTierEnabled)(db, 'ai-spawn'),
|
|
12
|
+
destructive: (0, mcp_tiers_1.isTierEnabled)(db, 'destructive'),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function redact(token) {
|
|
16
|
+
return token.length > 4 ? `…${token.slice(-4)}` : '…';
|
|
17
|
+
}
|
|
18
|
+
function createMcpAdminRouter(deps) {
|
|
19
|
+
const router = (0, express_1.Router)();
|
|
20
|
+
const { manager, desktopDb, desktopPort } = deps;
|
|
21
|
+
router.get('/status', (_req, res) => {
|
|
22
|
+
res.json({ ...manager.status(), tiers: tierState(desktopDb), tokenHint: redact((0, mcp_token_1.getMcpToken)()) });
|
|
23
|
+
});
|
|
24
|
+
router.post('/enable', async (_req, res) => {
|
|
25
|
+
try {
|
|
26
|
+
res.json(await manager.setEnabled(true));
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
res.status(409).json({ error: err instanceof Error ? err.message : 'Failed to enable MCP' });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
router.post('/disable', async (_req, res) => {
|
|
33
|
+
res.json(await manager.setEnabled(false));
|
|
34
|
+
});
|
|
35
|
+
// Update the opt-in permission tiers (read is always on, not settable here).
|
|
36
|
+
router.patch('/tiers', (req, res) => {
|
|
37
|
+
const body = (req.body ?? {});
|
|
38
|
+
const fields = [
|
|
39
|
+
['write', 'write'],
|
|
40
|
+
['aiSpawn', 'ai-spawn'],
|
|
41
|
+
['destructive', 'destructive'],
|
|
42
|
+
];
|
|
43
|
+
for (const [bodyKey, tier] of fields) {
|
|
44
|
+
if (bodyKey in body) {
|
|
45
|
+
if (typeof body[bodyKey] !== 'boolean') {
|
|
46
|
+
res.status(400).json({ error: `${bodyKey} must be a boolean` });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
(0, desktop_db_1.setDesktopSetting)(desktopDb, mcp_tiers_1.TIER_SETTING_KEY[tier], body[bodyKey] ? 'true' : 'false');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
res.json({ ok: true, tiers: tierState(desktopDb) });
|
|
53
|
+
});
|
|
54
|
+
// The full token — deliberately a separate loopback-only action so it never
|
|
55
|
+
// leaks through a general settings payload. Used by the panel's copy button.
|
|
56
|
+
router.get('/token', (_req, res) => {
|
|
57
|
+
res.json({ token: (0, mcp_token_1.getMcpToken)() });
|
|
58
|
+
});
|
|
59
|
+
router.post('/regenerate-token', (_req, res) => {
|
|
60
|
+
res.json({ token: (0, mcp_token_1.regenerateMcpToken)() });
|
|
61
|
+
});
|
|
62
|
+
// Connection info for the panel to render a ready-to-paste client config.
|
|
63
|
+
router.get('/config', (_req, res) => {
|
|
64
|
+
res.json({
|
|
65
|
+
httpUrl: `http://127.0.0.1:${desktopPort}/api/mcp`,
|
|
66
|
+
bridgeCommand: 'specrails-mcp',
|
|
67
|
+
hasToken: true,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
return router;
|
|
71
|
+
}
|