server.ts 100 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268
  1. /**
  2. * MCP Server Setup
  3. * Contains all the server configuration, tool schemas, and request handlers
  4. */
  5. import { Server } from '@modelcontextprotocol/sdk/server/index.js';
  6. import {
  7. CallToolRequestSchema,
  8. ListToolsRequestSchema,
  9. ListResourcesRequestSchema,
  10. ReadResourceRequestSchema,
  11. } from '@modelcontextprotocol/sdk/types.js';
  12. import { z } from 'zod';
  13. import { GogsClient } from './gogs-client.js';
  14. // Define tool schemas
  15. const GetUserSchema = z.object({
  16. username: z.string().describe('Username to get information for'),
  17. });
  18. const SearchUsersSchema = z.object({
  19. query: z.string().describe('Search query for users'),
  20. limit: z.number().optional().describe('Maximum number of results (default: 10)'),
  21. });
  22. const ListUserReposSchema = z.object({
  23. username: z.string().optional().describe('Username (omit for authenticated user)'),
  24. });
  25. const SearchReposSchema = z.object({
  26. query: z.string().describe('Search query for repositories'),
  27. uid: z.number().optional().describe('User ID to filter repositories'),
  28. limit: z.number().optional().describe('Maximum number of results (default: 10)'),
  29. page: z.number().optional().describe('Page number (default: 1)'),
  30. });
  31. const GetRepoSchema = z.object({
  32. owner: z.string().describe('Repository owner username'),
  33. repo: z.string().describe('Repository name'),
  34. });
  35. const CreateRepoSchema = z.object({
  36. name: z.string().describe('Repository name'),
  37. description: z.string().optional().describe('Repository description'),
  38. private: z.boolean().optional().describe('Create as private repository (default: false)'),
  39. auto_init: z.boolean().optional().describe('Initialize with README (default: false)'),
  40. gitignores: z.string().optional().describe('Gitignore templates (e.g., "Go,VisualStudioCode")'),
  41. license: z.string().optional().describe('License template (e.g., "MIT License")'),
  42. readme: z.string().optional().describe('README template (default: "Default")'),
  43. });
  44. const DeleteRepoSchema = z.object({
  45. owner: z.string().describe('Repository owner username'),
  46. repo: z.string().describe('Repository name'),
  47. });
  48. const GetContentsSchema = z.object({
  49. owner: z.string().describe('Repository owner username'),
  50. repo: z.string().describe('Repository name'),
  51. path: z.string().describe('File or directory path'),
  52. ref: z.string().optional().describe('Branch, tag, or commit SHA (default: default branch)'),
  53. });
  54. const GetRawContentSchema = z.object({
  55. owner: z.string().describe('Repository owner username'),
  56. repo: z.string().describe('Repository name'),
  57. ref: z.string().describe('Branch, tag, or commit SHA'),
  58. path: z.string().describe('File path'),
  59. });
  60. const ListBranchesSchema = z.object({
  61. owner: z.string().describe('Repository owner username'),
  62. repo: z.string().describe('Repository name'),
  63. });
  64. const GetCommitsSchema = z.object({
  65. owner: z.string().describe('Repository owner username'),
  66. repo: z.string().describe('Repository name'),
  67. sha: z.string().optional().describe('Branch name or commit SHA'),
  68. page: z.number().optional().describe('Page number (default: 1)'),
  69. });
  70. const GetTreeSchema = z.object({
  71. owner: z.string().describe('Repository owner username'),
  72. repo: z.string().describe('Repository name'),
  73. sha: z.string().describe('Git tree SHA or commit SHA'),
  74. });
  75. const ListIssuesSchema = z.object({
  76. owner: z.string().describe('Repository owner username'),
  77. repo: z.string().describe('Repository name'),
  78. state: z.enum(['open', 'closed', 'all']).optional().describe('Filter by state (default: open)'),
  79. labels: z.string().optional().describe('Comma-separated list of label names'),
  80. page: z.number().optional().describe('Page number (default: 1)'),
  81. per_page: z.number().optional().describe('Results per page (default: 30)'),
  82. });
  83. const GetIssueSchema = z.object({
  84. owner: z.string().describe('Repository owner username'),
  85. repo: z.string().describe('Repository name'),
  86. number: z.number().describe('Issue number'),
  87. });
  88. const CreateIssueSchema = z.object({
  89. owner: z.string().describe('Repository owner username'),
  90. repo: z.string().describe('Repository name'),
  91. title: z.string().describe('Issue title'),
  92. body: z.string().optional().describe('Issue description'),
  93. assignee: z.string().optional().describe('Username to assign the issue to'),
  94. milestone: z.number().optional().describe('Milestone ID'),
  95. labels: z.array(z.number()).optional().describe('Array of label IDs'),
  96. });
  97. const UpdateIssueSchema = z.object({
  98. owner: z.string().describe('Repository owner username'),
  99. repo: z.string().describe('Repository name'),
  100. number: z.number().describe('Issue number'),
  101. title: z.string().optional().describe('Issue title'),
  102. body: z.string().optional().describe('Issue description'),
  103. assignee: z.string().optional().describe('Username to assign the issue to'),
  104. milestone: z.number().optional().describe('Milestone ID'),
  105. state: z.enum(['open', 'closed']).optional().describe('Issue state'),
  106. labels: z.array(z.number()).optional().describe('Array of label IDs'),
  107. });
  108. const ListIssueCommentsSchema = z.object({
  109. owner: z.string().describe('Repository owner username'),
  110. repo: z.string().describe('Repository name'),
  111. number: z.number().describe('Issue number'),
  112. });
  113. const CreateIssueCommentSchema = z.object({
  114. owner: z.string().describe('Repository owner username'),
  115. repo: z.string().describe('Repository name'),
  116. number: z.number().describe('Issue number'),
  117. body: z.string().describe('Comment text'),
  118. });
  119. const UpdateIssueCommentSchema = z.object({
  120. owner: z.string().describe('Repository owner username'),
  121. repo: z.string().describe('Repository name'),
  122. commentId: z.number().describe('Comment ID'),
  123. body: z.string().describe('Updated comment text'),
  124. });
  125. const ListLabelsSchema = z.object({
  126. owner: z.string().describe('Repository owner username'),
  127. repo: z.string().describe('Repository name'),
  128. });
  129. const GetLabelSchema = z.object({
  130. owner: z.string().describe('Repository owner username'),
  131. repo: z.string().describe('Repository name'),
  132. labelId: z.number().describe('Label ID'),
  133. });
  134. const CreateLabelSchema = z.object({
  135. owner: z.string().describe('Repository owner username'),
  136. repo: z.string().describe('Repository name'),
  137. name: z.string().describe('Label name'),
  138. color: z.string().describe('7-character hex color code with leading # (e.g., #ff0000)'),
  139. });
  140. const UpdateLabelSchema = z.object({
  141. owner: z.string().describe('Repository owner username'),
  142. repo: z.string().describe('Repository name'),
  143. labelId: z.number().describe('Label ID'),
  144. name: z.string().optional().describe('Label name'),
  145. color: z.string().optional().describe('7-character hex color code with leading # (e.g., #ff0000)'),
  146. });
  147. const DeleteLabelSchema = z.object({
  148. owner: z.string().describe('Repository owner username'),
  149. repo: z.string().describe('Repository name'),
  150. labelId: z.number().describe('Label ID'),
  151. });
  152. const ListIssueLabelsSchema = z.object({
  153. owner: z.string().describe('Repository owner username'),
  154. repo: z.string().describe('Repository name'),
  155. number: z.number().describe('Issue number'),
  156. });
  157. const AddIssueLabelsSchema = z.object({
  158. owner: z.string().describe('Repository owner username'),
  159. repo: z.string().describe('Repository name'),
  160. number: z.number().describe('Issue number'),
  161. labels: z.array(z.number()).describe('Array of label IDs to add'),
  162. });
  163. const RemoveIssueLabelSchema = z.object({
  164. owner: z.string().describe('Repository owner username'),
  165. repo: z.string().describe('Repository name'),
  166. number: z.number().describe('Issue number'),
  167. labelId: z.number().describe('Label ID to remove'),
  168. });
  169. const ReplaceIssueLabelsSchema = z.object({
  170. owner: z.string().describe('Repository owner username'),
  171. repo: z.string().describe('Repository name'),
  172. number: z.number().describe('Issue number'),
  173. labels: z.array(z.number()).describe('Array of label IDs to set (replaces all existing labels)'),
  174. });
  175. const RemoveAllIssueLabelsSchema = z.object({
  176. owner: z.string().describe('Repository owner username'),
  177. repo: z.string().describe('Repository name'),
  178. number: z.number().describe('Issue number'),
  179. });
  180. const ListMilestonesSchema = z.object({
  181. owner: z.string().describe('Repository owner username'),
  182. repo: z.string().describe('Repository name'),
  183. });
  184. const GetMilestoneSchema = z.object({
  185. owner: z.string().describe('Repository owner username'),
  186. repo: z.string().describe('Repository name'),
  187. milestoneId: z.number().describe('Milestone ID'),
  188. });
  189. const CreateMilestoneSchema = z.object({
  190. owner: z.string().describe('Repository owner username'),
  191. repo: z.string().describe('Repository name'),
  192. title: z.string().describe('Milestone title'),
  193. description: z.string().optional().describe('Milestone description'),
  194. due_on: z.string().optional().describe('Due date in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)'),
  195. });
  196. const UpdateMilestoneSchema = z.object({
  197. owner: z.string().describe('Repository owner username'),
  198. repo: z.string().describe('Repository name'),
  199. milestoneId: z.number().describe('Milestone ID'),
  200. title: z.string().optional().describe('Milestone title'),
  201. description: z.string().optional().describe('Milestone description'),
  202. due_on: z.string().optional().describe('Due date in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)'),
  203. state: z.enum(['open', 'closed']).optional().describe('Milestone state'),
  204. });
  205. const DeleteMilestoneSchema = z.object({
  206. owner: z.string().describe('Repository owner username'),
  207. repo: z.string().describe('Repository name'),
  208. milestoneId: z.number().describe('Milestone ID'),
  209. });
  210. const CreateOrganizationSchema = z.object({
  211. username: z.string().describe('Organization username (alphanumeric, dashes, underscores)'),
  212. full_name: z.string().optional().describe('Organization full name'),
  213. description: z.string().optional().describe('Organization description'),
  214. website: z.string().optional().describe('Organization website'),
  215. location: z.string().optional().describe('Organization location'),
  216. });
  217. const ListPublicOrganizationsSchema = z.object({
  218. username: z.string().describe('Username to get public organizations for'),
  219. });
  220. const GetOrganizationSchema = z.object({
  221. orgname: z.string().describe('Organization name'),
  222. });
  223. const UpdateOrganizationSchema = z.object({
  224. orgname: z.string().describe('Organization name'),
  225. full_name: z.string().optional().describe('Organization full name'),
  226. description: z.string().optional().describe('Organization description'),
  227. website: z.string().optional().describe('Organization website'),
  228. location: z.string().optional().describe('Organization location'),
  229. });
  230. const AddOrganizationMemberSchema = z.object({
  231. orgname: z.string().describe('Organization name'),
  232. username: z.string().describe('Username to add as member'),
  233. role: z.enum(['admin', 'member']).describe('Member role (admin or member)'),
  234. });
  235. const ListOrganizationTeamsSchema = z.object({
  236. orgname: z.string().describe('Organization name'),
  237. });
  238. const CreateTeamSchema = z.object({
  239. orgname: z.string().describe('Organization name'),
  240. name: z.string().describe('Team name (max 30 characters, alphanumeric with dashes and dots)'),
  241. description: z.string().optional().describe('Team description (max 255 characters)'),
  242. permission: z.enum(['read', 'write', 'admin']).optional().describe('Team permission level (default: read)'),
  243. });
  244. const GetTeamSchema = z.object({
  245. teamId: z.number().describe('Team ID'),
  246. });
  247. const AddTeamMemberSchema = z.object({
  248. teamId: z.number().describe('Team ID'),
  249. username: z.string().describe('Username to add to the team'),
  250. });
  251. const RemoveTeamMemberSchema = z.object({
  252. teamId: z.number().describe('Team ID'),
  253. username: z.string().describe('Username to remove from the team'),
  254. });
  255. const ListReleasesSchema = z.object({
  256. owner: z.string().describe('Repository owner username'),
  257. repo: z.string().describe('Repository name'),
  258. });
  259. const ListCollaboratorsSchema = z.object({
  260. owner: z.string().describe('Repository owner username'),
  261. repo: z.string().describe('Repository name'),
  262. });
  263. const CheckCollaboratorSchema = z.object({
  264. owner: z.string().describe('Repository owner username'),
  265. repo: z.string().describe('Repository name'),
  266. username: z.string().describe('Username to check for collaborator status'),
  267. });
  268. const AddCollaboratorSchema = z.object({
  269. owner: z.string().describe('Repository owner username'),
  270. repo: z.string().describe('Repository name'),
  271. username: z.string().describe('Username to add as collaborator'),
  272. permission: z.enum(['read', 'write', 'admin']).optional().describe('Permission level (default: write)'),
  273. });
  274. const RemoveCollaboratorSchema = z.object({
  275. owner: z.string().describe('Repository owner username'),
  276. repo: z.string().describe('Repository name'),
  277. username: z.string().describe('Username to remove as collaborator'),
  278. });
  279. const AddUserEmailsSchema = z.object({
  280. emails: z.array(z.string()).describe('Array of email addresses to add'),
  281. });
  282. const DeleteUserEmailsSchema = z.object({
  283. emails: z.array(z.string()).describe('Array of email addresses to delete'),
  284. });
  285. const ListUserKeysSchema = z.object({
  286. username: z.string().describe('Username to list public keys for'),
  287. });
  288. const GetPublicKeySchema = z.object({
  289. keyId: z.number().describe('Public key ID'),
  290. });
  291. const CreatePublicKeySchema = z.object({
  292. title: z.string().describe('Key title/name for identification'),
  293. key: z.string().describe('The public key content (SSH public key format)'),
  294. });
  295. const DeletePublicKeySchema = z.object({
  296. keyId: z.number().describe('Public key ID to delete'),
  297. });
  298. const ListHooksSchema = z.object({
  299. owner: z.string().describe('Repository owner username'),
  300. repo: z.string().describe('Repository name'),
  301. });
  302. const CreateHookSchema = z.object({
  303. owner: z.string().describe('Repository owner username'),
  304. repo: z.string().describe('Repository name'),
  305. type: z.enum(['gogs', 'slack']).describe('Webhook type (gogs or slack)'),
  306. config: z.object({
  307. url: z.string().describe('URL to send webhook payloads to'),
  308. content_type: z.enum(['json', 'form']).describe('Content type for payload (json or form)'),
  309. secret: z.string().optional().describe('Optional secret for webhook validation'),
  310. channel: z.string().optional().describe('Slack channel (for slack webhooks)'),
  311. username: z.string().optional().describe('Slack username (for slack webhooks)'),
  312. icon_url: z.string().optional().describe('Slack icon URL (for slack webhooks)'),
  313. color: z.string().optional().describe('Slack color (for slack webhooks)'),
  314. }).describe('Webhook configuration'),
  315. events: z.array(z.enum(['create', 'delete', 'fork', 'push', 'issues', 'issue_comment', 'pull_request', 'release']))
  316. .optional()
  317. .describe('Events that trigger the webhook (default: ["push"])'),
  318. active: z.boolean().optional().describe('Whether the webhook is active (default: false)'),
  319. });
  320. const UpdateHookSchema = z.object({
  321. owner: z.string().describe('Repository owner username'),
  322. repo: z.string().describe('Repository name'),
  323. hookId: z.number().describe('Webhook ID to update'),
  324. config: z.object({
  325. url: z.string().describe('URL to send webhook payloads to'),
  326. content_type: z.enum(['json', 'form']).describe('Content type for payload (json or form)'),
  327. secret: z.string().optional().describe('Optional secret for webhook validation'),
  328. channel: z.string().optional().describe('Slack channel (for slack webhooks)'),
  329. username: z.string().optional().describe('Slack username (for slack webhooks)'),
  330. icon_url: z.string().optional().describe('Slack icon URL (for slack webhooks)'),
  331. color: z.string().optional().describe('Slack color (for slack webhooks)'),
  332. }).describe('Webhook configuration'),
  333. events: z.array(z.enum(['create', 'delete', 'fork', 'push', 'issues', 'issue_comment', 'pull_request', 'release']))
  334. .optional()
  335. .describe('Events that trigger the webhook'),
  336. active: z.boolean().optional().describe('Whether the webhook is active'),
  337. });
  338. const DeleteHookSchema = z.object({
  339. owner: z.string().describe('Repository owner username'),
  340. repo: z.string().describe('Repository name'),
  341. hookId: z.number().describe('Webhook ID to delete'),
  342. });
  343. const ListFollowersSchema = z.object({
  344. username: z.string().describe('Username to list followers for'),
  345. });
  346. const ListFollowingSchema = z.object({
  347. username: z.string().describe('Username to list following for'),
  348. });
  349. const CheckFollowingSchema = z.object({
  350. username: z.string().describe('Username to check if authenticated user is following'),
  351. });
  352. const FollowUserSchema = z.object({
  353. username: z.string().describe('Username to follow'),
  354. });
  355. const UnfollowUserSchema = z.object({
  356. username: z.string().describe('Username to unfollow'),
  357. });
  358. const ListDeployKeysSchema = z.object({
  359. owner: z.string().describe('Repository owner username'),
  360. repo: z.string().describe('Repository name'),
  361. });
  362. const GetDeployKeySchema = z.object({
  363. owner: z.string().describe('Repository owner username'),
  364. repo: z.string().describe('Repository name'),
  365. keyId: z.number().describe('Deploy key ID'),
  366. });
  367. const CreateDeployKeySchema = z.object({
  368. owner: z.string().describe('Repository owner username'),
  369. repo: z.string().describe('Repository name'),
  370. title: z.string().describe('Deploy key title/name for identification'),
  371. key: z.string().describe('The public key content (SSH public key format)'),
  372. read_only: z.boolean().optional().describe('Whether the key is read-only (default: true)'),
  373. });
  374. const DeleteDeployKeySchema = z.object({
  375. owner: z.string().describe('Repository owner username'),
  376. repo: z.string().describe('Repository name'),
  377. keyId: z.number().describe('Deploy key ID to delete'),
  378. });
  379. const RenderMarkdownSchema = z.object({
  380. text: z.string().describe('Markdown text to render'),
  381. mode: z.enum(['gfm', 'markdown']).optional().describe('Rendering mode: "gfm" for GitHub Flavored Markdown (default) or "markdown" for standard Markdown'),
  382. context: z.string().optional().describe('Repository context in the format "owner/repo" for repository-aware rendering (e.g., for issue references)'),
  383. });
  384. const RenderMarkdownRawSchema = z.object({
  385. text: z.string().describe('Raw markdown text to render to HTML'),
  386. });
  387. /**
  388. * Create and configure an MCP server with all tools and handlers
  389. */
  390. export function createMcpServer(gogsClient: GogsClient): Server {
  391. const server = new Server(
  392. {
  393. name: 'gogs-mcp-server',
  394. version: '1.0.0',
  395. },
  396. {
  397. capabilities: {
  398. tools: {},
  399. resources: {},
  400. },
  401. }
  402. );
  403. // Register tools
  404. server.setRequestHandler(ListToolsRequestSchema, async () => {
  405. return {
  406. tools: [
  407. {
  408. name: 'get_current_user',
  409. description: 'Get information about the authenticated user',
  410. inputSchema: {
  411. type: 'object',
  412. properties: {},
  413. },
  414. },
  415. {
  416. name: 'get_user',
  417. description: 'Get information about a specific user',
  418. inputSchema: {
  419. type: 'object',
  420. properties: {
  421. username: {
  422. type: 'string',
  423. description: 'Username to get information for',
  424. },
  425. },
  426. required: ['username'],
  427. },
  428. },
  429. {
  430. name: 'search_users',
  431. description: 'Search for users by username',
  432. inputSchema: {
  433. type: 'object',
  434. properties: {
  435. query: {
  436. type: 'string',
  437. description: 'Search query for users',
  438. },
  439. limit: {
  440. type: 'number',
  441. description: 'Maximum number of results (default: 10)',
  442. },
  443. },
  444. required: ['query'],
  445. },
  446. },
  447. {
  448. name: 'list_user_repositories',
  449. description: 'List repositories for a user (or authenticated user if no username provided)',
  450. inputSchema: {
  451. type: 'object',
  452. properties: {
  453. username: {
  454. type: 'string',
  455. description: 'Username (omit for authenticated user)',
  456. },
  457. },
  458. },
  459. },
  460. {
  461. name: 'search_repositories',
  462. description: 'Search for repositories',
  463. inputSchema: {
  464. type: 'object',
  465. properties: {
  466. query: {
  467. type: 'string',
  468. description: 'Search query for repositories',
  469. },
  470. uid: {
  471. type: 'number',
  472. description: 'User ID to filter repositories',
  473. },
  474. limit: {
  475. type: 'number',
  476. description: 'Maximum number of results (default: 10)',
  477. },
  478. page: {
  479. type: 'number',
  480. description: 'Page number (default: 1)',
  481. },
  482. },
  483. required: ['query'],
  484. },
  485. },
  486. {
  487. name: 'get_repository',
  488. description: 'Get information about a specific repository',
  489. inputSchema: {
  490. type: 'object',
  491. properties: {
  492. owner: {
  493. type: 'string',
  494. description: 'Repository owner username',
  495. },
  496. repo: {
  497. type: 'string',
  498. description: 'Repository name',
  499. },
  500. },
  501. required: ['owner', 'repo'],
  502. },
  503. },
  504. {
  505. name: 'create_repository',
  506. description: 'Create a new repository for the authenticated user',
  507. inputSchema: {
  508. type: 'object',
  509. properties: {
  510. name: {
  511. type: 'string',
  512. description: 'Repository name',
  513. },
  514. description: {
  515. type: 'string',
  516. description: 'Repository description',
  517. },
  518. private: {
  519. type: 'boolean',
  520. description: 'Create as private repository (default: false)',
  521. },
  522. auto_init: {
  523. type: 'boolean',
  524. description: 'Initialize with README (default: false)',
  525. },
  526. gitignores: {
  527. type: 'string',
  528. description: 'Gitignore templates (e.g., "Go,VisualStudioCode")',
  529. },
  530. license: {
  531. type: 'string',
  532. description: 'License template (e.g., "MIT License")',
  533. },
  534. readme: {
  535. type: 'string',
  536. description: 'README template (default: "Default")',
  537. },
  538. },
  539. required: ['name'],
  540. },
  541. },
  542. {
  543. name: 'delete_repository',
  544. description: 'Delete a repository (requires admin access)',
  545. inputSchema: {
  546. type: 'object',
  547. properties: {
  548. owner: {
  549. type: 'string',
  550. description: 'Repository owner username',
  551. },
  552. repo: {
  553. type: 'string',
  554. description: 'Repository name',
  555. },
  556. },
  557. required: ['owner', 'repo'],
  558. },
  559. },
  560. {
  561. name: 'get_contents',
  562. description: 'Get file or directory contents from a repository',
  563. inputSchema: {
  564. type: 'object',
  565. properties: {
  566. owner: {
  567. type: 'string',
  568. description: 'Repository owner username',
  569. },
  570. repo: {
  571. type: 'string',
  572. description: 'Repository name',
  573. },
  574. path: {
  575. type: 'string',
  576. description: 'File or directory path',
  577. },
  578. ref: {
  579. type: 'string',
  580. description: 'Branch, tag, or commit SHA (default: default branch)',
  581. },
  582. },
  583. required: ['owner', 'repo', 'path'],
  584. },
  585. },
  586. {
  587. name: 'get_raw_content',
  588. description: 'Get raw file content from a repository',
  589. inputSchema: {
  590. type: 'object',
  591. properties: {
  592. owner: {
  593. type: 'string',
  594. description: 'Repository owner username',
  595. },
  596. repo: {
  597. type: 'string',
  598. description: 'Repository name',
  599. },
  600. ref: {
  601. type: 'string',
  602. description: 'Branch, tag, or commit SHA',
  603. },
  604. path: {
  605. type: 'string',
  606. description: 'File path',
  607. },
  608. },
  609. required: ['owner', 'repo', 'ref', 'path'],
  610. },
  611. },
  612. {
  613. name: 'list_branches',
  614. description: 'List all branches in a repository',
  615. inputSchema: {
  616. type: 'object',
  617. properties: {
  618. owner: {
  619. type: 'string',
  620. description: 'Repository owner username',
  621. },
  622. repo: {
  623. type: 'string',
  624. description: 'Repository name',
  625. },
  626. },
  627. required: ['owner', 'repo'],
  628. },
  629. },
  630. {
  631. name: 'get_commits',
  632. description: 'Get commit history from a repository',
  633. inputSchema: {
  634. type: 'object',
  635. properties: {
  636. owner: {
  637. type: 'string',
  638. description: 'Repository owner username',
  639. },
  640. repo: {
  641. type: 'string',
  642. description: 'Repository name',
  643. },
  644. sha: {
  645. type: 'string',
  646. description: 'Branch name or commit SHA',
  647. },
  648. page: {
  649. type: 'number',
  650. description: 'Page number (default: 1)',
  651. },
  652. },
  653. required: ['owner', 'repo'],
  654. },
  655. },
  656. {
  657. name: 'get_tree',
  658. description: 'Get a git tree by SHA - provides low-level access to repository structure at a specific commit or tree SHA',
  659. inputSchema: {
  660. type: 'object',
  661. properties: {
  662. owner: {
  663. type: 'string',
  664. description: 'Repository owner username',
  665. },
  666. repo: {
  667. type: 'string',
  668. description: 'Repository name',
  669. },
  670. sha: {
  671. type: 'string',
  672. description: 'Git tree SHA or commit SHA',
  673. },
  674. },
  675. required: ['owner', 'repo', 'sha'],
  676. },
  677. },
  678. {
  679. name: 'list_issues',
  680. description: 'List issues in a repository',
  681. inputSchema: {
  682. type: 'object',
  683. properties: {
  684. owner: {
  685. type: 'string',
  686. description: 'Repository owner username',
  687. },
  688. repo: {
  689. type: 'string',
  690. description: 'Repository name',
  691. },
  692. state: {
  693. type: 'string',
  694. enum: ['open', 'closed', 'all'],
  695. description: 'Filter by state (default: open)',
  696. },
  697. labels: {
  698. type: 'string',
  699. description: 'Comma-separated list of label names',
  700. },
  701. page: {
  702. type: 'number',
  703. description: 'Page number (default: 1)',
  704. },
  705. per_page: {
  706. type: 'number',
  707. description: 'Results per page (default: 30)',
  708. },
  709. },
  710. required: ['owner', 'repo'],
  711. },
  712. },
  713. {
  714. name: 'get_issue',
  715. description: 'Get a specific issue by number',
  716. inputSchema: {
  717. type: 'object',
  718. properties: {
  719. owner: {
  720. type: 'string',
  721. description: 'Repository owner username',
  722. },
  723. repo: {
  724. type: 'string',
  725. description: 'Repository name',
  726. },
  727. number: {
  728. type: 'number',
  729. description: 'Issue number',
  730. },
  731. },
  732. required: ['owner', 'repo', 'number'],
  733. },
  734. },
  735. {
  736. name: 'create_issue',
  737. description: 'Create a new issue in a repository',
  738. inputSchema: {
  739. type: 'object',
  740. properties: {
  741. owner: {
  742. type: 'string',
  743. description: 'Repository owner username',
  744. },
  745. repo: {
  746. type: 'string',
  747. description: 'Repository name',
  748. },
  749. title: {
  750. type: 'string',
  751. description: 'Issue title',
  752. },
  753. body: {
  754. type: 'string',
  755. description: 'Issue description',
  756. },
  757. assignee: {
  758. type: 'string',
  759. description: 'Username to assign the issue to',
  760. },
  761. milestone: {
  762. type: 'number',
  763. description: 'Milestone ID',
  764. },
  765. labels: {
  766. type: 'array',
  767. items: {
  768. type: 'number',
  769. },
  770. description: 'Array of label IDs',
  771. },
  772. },
  773. required: ['owner', 'repo', 'title'],
  774. },
  775. },
  776. {
  777. name: 'update_issue',
  778. description: 'Update an existing issue (including closing or reopening)',
  779. inputSchema: {
  780. type: 'object',
  781. properties: {
  782. owner: {
  783. type: 'string',
  784. description: 'Repository owner username',
  785. },
  786. repo: {
  787. type: 'string',
  788. description: 'Repository name',
  789. },
  790. number: {
  791. type: 'number',
  792. description: 'Issue number',
  793. },
  794. title: {
  795. type: 'string',
  796. description: 'Issue title',
  797. },
  798. body: {
  799. type: 'string',
  800. description: 'Issue description',
  801. },
  802. assignee: {
  803. type: 'string',
  804. description: 'Username to assign the issue to',
  805. },
  806. milestone: {
  807. type: 'number',
  808. description: 'Milestone ID',
  809. },
  810. state: {
  811. type: 'string',
  812. enum: ['open', 'closed'],
  813. description: 'Issue state',
  814. },
  815. labels: {
  816. type: 'array',
  817. items: {
  818. type: 'number',
  819. },
  820. description: 'Array of label IDs',
  821. },
  822. },
  823. required: ['owner', 'repo', 'number'],
  824. },
  825. },
  826. {
  827. name: 'list_issue_comments',
  828. description: 'List all comments on an issue',
  829. inputSchema: {
  830. type: 'object',
  831. properties: {
  832. owner: {
  833. type: 'string',
  834. description: 'Repository owner username',
  835. },
  836. repo: {
  837. type: 'string',
  838. description: 'Repository name',
  839. },
  840. number: {
  841. type: 'number',
  842. description: 'Issue number',
  843. },
  844. },
  845. required: ['owner', 'repo', 'number'],
  846. },
  847. },
  848. {
  849. name: 'create_issue_comment',
  850. description: 'Add a comment to an issue',
  851. inputSchema: {
  852. type: 'object',
  853. properties: {
  854. owner: {
  855. type: 'string',
  856. description: 'Repository owner username',
  857. },
  858. repo: {
  859. type: 'string',
  860. description: 'Repository name',
  861. },
  862. number: {
  863. type: 'number',
  864. description: 'Issue number',
  865. },
  866. body: {
  867. type: 'string',
  868. description: 'Comment text',
  869. },
  870. },
  871. required: ['owner', 'repo', 'number', 'body'],
  872. },
  873. },
  874. {
  875. name: 'update_issue_comment',
  876. description: 'Edit an existing comment on an issue',
  877. inputSchema: {
  878. type: 'object',
  879. properties: {
  880. owner: {
  881. type: 'string',
  882. description: 'Repository owner username',
  883. },
  884. repo: {
  885. type: 'string',
  886. description: 'Repository name',
  887. },
  888. commentId: {
  889. type: 'number',
  890. description: 'Comment ID',
  891. },
  892. body: {
  893. type: 'string',
  894. description: 'Updated comment text',
  895. },
  896. },
  897. required: ['owner', 'repo', 'commentId', 'body'],
  898. },
  899. },
  900. {
  901. name: 'list_labels',
  902. description: 'List all labels in a repository',
  903. inputSchema: {
  904. type: 'object',
  905. properties: {
  906. owner: {
  907. type: 'string',
  908. description: 'Repository owner username',
  909. },
  910. repo: {
  911. type: 'string',
  912. description: 'Repository name',
  913. },
  914. },
  915. required: ['owner', 'repo'],
  916. },
  917. },
  918. {
  919. name: 'get_label',
  920. description: 'Get a specific label by ID',
  921. inputSchema: {
  922. type: 'object',
  923. properties: {
  924. owner: {
  925. type: 'string',
  926. description: 'Repository owner username',
  927. },
  928. repo: {
  929. type: 'string',
  930. description: 'Repository name',
  931. },
  932. labelId: {
  933. type: 'number',
  934. description: 'Label ID',
  935. },
  936. },
  937. required: ['owner', 'repo', 'labelId'],
  938. },
  939. },
  940. {
  941. name: 'create_label',
  942. description: 'Create a new label in a repository',
  943. inputSchema: {
  944. type: 'object',
  945. properties: {
  946. owner: {
  947. type: 'string',
  948. description: 'Repository owner username',
  949. },
  950. repo: {
  951. type: 'string',
  952. description: 'Repository name',
  953. },
  954. name: {
  955. type: 'string',
  956. description: 'Label name',
  957. },
  958. color: {
  959. type: 'string',
  960. description: '7-character hex color code with leading # (e.g., #ff0000)',
  961. },
  962. },
  963. required: ['owner', 'repo', 'name', 'color'],
  964. },
  965. },
  966. {
  967. name: 'update_label',
  968. description: 'Update an existing label',
  969. inputSchema: {
  970. type: 'object',
  971. properties: {
  972. owner: {
  973. type: 'string',
  974. description: 'Repository owner username',
  975. },
  976. repo: {
  977. type: 'string',
  978. description: 'Repository name',
  979. },
  980. labelId: {
  981. type: 'number',
  982. description: 'Label ID',
  983. },
  984. name: {
  985. type: 'string',
  986. description: 'Label name',
  987. },
  988. color: {
  989. type: 'string',
  990. description: '7-character hex color code with leading # (e.g., #ff0000)',
  991. },
  992. },
  993. required: ['owner', 'repo', 'labelId'],
  994. },
  995. },
  996. {
  997. name: 'delete_label',
  998. description: 'Delete a label from a repository',
  999. inputSchema: {
  1000. type: 'object',
  1001. properties: {
  1002. owner: {
  1003. type: 'string',
  1004. description: 'Repository owner username',
  1005. },
  1006. repo: {
  1007. type: 'string',
  1008. description: 'Repository name',
  1009. },
  1010. labelId: {
  1011. type: 'number',
  1012. description: 'Label ID',
  1013. },
  1014. },
  1015. required: ['owner', 'repo', 'labelId'],
  1016. },
  1017. },
  1018. {
  1019. name: 'list_issue_labels',
  1020. description: 'List all labels on a specific issue',
  1021. inputSchema: {
  1022. type: 'object',
  1023. properties: {
  1024. owner: {
  1025. type: 'string',
  1026. description: 'Repository owner username',
  1027. },
  1028. repo: {
  1029. type: 'string',
  1030. description: 'Repository name',
  1031. },
  1032. number: {
  1033. type: 'number',
  1034. description: 'Issue number',
  1035. },
  1036. },
  1037. required: ['owner', 'repo', 'number'],
  1038. },
  1039. },
  1040. {
  1041. name: 'add_issue_labels',
  1042. description: 'Add labels to an issue',
  1043. inputSchema: {
  1044. type: 'object',
  1045. properties: {
  1046. owner: {
  1047. type: 'string',
  1048. description: 'Repository owner username',
  1049. },
  1050. repo: {
  1051. type: 'string',
  1052. description: 'Repository name',
  1053. },
  1054. number: {
  1055. type: 'number',
  1056. description: 'Issue number',
  1057. },
  1058. labels: {
  1059. type: 'array',
  1060. items: {
  1061. type: 'number',
  1062. },
  1063. description: 'Array of label IDs to add',
  1064. },
  1065. },
  1066. required: ['owner', 'repo', 'number', 'labels'],
  1067. },
  1068. },
  1069. {
  1070. name: 'remove_issue_label',
  1071. description: 'Remove a specific label from an issue',
  1072. inputSchema: {
  1073. type: 'object',
  1074. properties: {
  1075. owner: {
  1076. type: 'string',
  1077. description: 'Repository owner username',
  1078. },
  1079. repo: {
  1080. type: 'string',
  1081. description: 'Repository name',
  1082. },
  1083. number: {
  1084. type: 'number',
  1085. description: 'Issue number',
  1086. },
  1087. labelId: {
  1088. type: 'number',
  1089. description: 'Label ID to remove',
  1090. },
  1091. },
  1092. required: ['owner', 'repo', 'number', 'labelId'],
  1093. },
  1094. },
  1095. {
  1096. name: 'replace_issue_labels',
  1097. description: 'Replace all labels on an issue with a new set',
  1098. inputSchema: {
  1099. type: 'object',
  1100. properties: {
  1101. owner: {
  1102. type: 'string',
  1103. description: 'Repository owner username',
  1104. },
  1105. repo: {
  1106. type: 'string',
  1107. description: 'Repository name',
  1108. },
  1109. number: {
  1110. type: 'number',
  1111. description: 'Issue number',
  1112. },
  1113. labels: {
  1114. type: 'array',
  1115. items: {
  1116. type: 'number',
  1117. },
  1118. description: 'Array of label IDs to set (replaces all existing labels)',
  1119. },
  1120. },
  1121. required: ['owner', 'repo', 'number', 'labels'],
  1122. },
  1123. },
  1124. {
  1125. name: 'remove_all_issue_labels',
  1126. description: 'Remove all labels from an issue',
  1127. inputSchema: {
  1128. type: 'object',
  1129. properties: {
  1130. owner: {
  1131. type: 'string',
  1132. description: 'Repository owner username',
  1133. },
  1134. repo: {
  1135. type: 'string',
  1136. description: 'Repository name',
  1137. },
  1138. number: {
  1139. type: 'number',
  1140. description: 'Issue number',
  1141. },
  1142. },
  1143. required: ['owner', 'repo', 'number'],
  1144. },
  1145. },
  1146. {
  1147. name: 'list_milestones',
  1148. description: 'List all milestones in a repository',
  1149. inputSchema: {
  1150. type: 'object',
  1151. properties: {
  1152. owner: {
  1153. type: 'string',
  1154. description: 'Repository owner username',
  1155. },
  1156. repo: {
  1157. type: 'string',
  1158. description: 'Repository name',
  1159. },
  1160. },
  1161. required: ['owner', 'repo'],
  1162. },
  1163. },
  1164. {
  1165. name: 'get_milestone',
  1166. description: 'Get a specific milestone by ID',
  1167. inputSchema: {
  1168. type: 'object',
  1169. properties: {
  1170. owner: {
  1171. type: 'string',
  1172. description: 'Repository owner username',
  1173. },
  1174. repo: {
  1175. type: 'string',
  1176. description: 'Repository name',
  1177. },
  1178. milestoneId: {
  1179. type: 'number',
  1180. description: 'Milestone ID',
  1181. },
  1182. },
  1183. required: ['owner', 'repo', 'milestoneId'],
  1184. },
  1185. },
  1186. {
  1187. name: 'create_milestone',
  1188. description: 'Create a new milestone in a repository',
  1189. inputSchema: {
  1190. type: 'object',
  1191. properties: {
  1192. owner: {
  1193. type: 'string',
  1194. description: 'Repository owner username',
  1195. },
  1196. repo: {
  1197. type: 'string',
  1198. description: 'Repository name',
  1199. },
  1200. title: {
  1201. type: 'string',
  1202. description: 'Milestone title',
  1203. },
  1204. description: {
  1205. type: 'string',
  1206. description: 'Milestone description',
  1207. },
  1208. due_on: {
  1209. type: 'string',
  1210. description: 'Due date in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)',
  1211. },
  1212. },
  1213. required: ['owner', 'repo', 'title'],
  1214. },
  1215. },
  1216. {
  1217. name: 'update_milestone',
  1218. description: 'Update an existing milestone',
  1219. inputSchema: {
  1220. type: 'object',
  1221. properties: {
  1222. owner: {
  1223. type: 'string',
  1224. description: 'Repository owner username',
  1225. },
  1226. repo: {
  1227. type: 'string',
  1228. description: 'Repository name',
  1229. },
  1230. milestoneId: {
  1231. type: 'number',
  1232. description: 'Milestone ID',
  1233. },
  1234. title: {
  1235. type: 'string',
  1236. description: 'Milestone title',
  1237. },
  1238. description: {
  1239. type: 'string',
  1240. description: 'Milestone description',
  1241. },
  1242. due_on: {
  1243. type: 'string',
  1244. description: 'Due date in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)',
  1245. },
  1246. state: {
  1247. type: 'string',
  1248. enum: ['open', 'closed'],
  1249. description: 'Milestone state',
  1250. },
  1251. },
  1252. required: ['owner', 'repo', 'milestoneId'],
  1253. },
  1254. },
  1255. {
  1256. name: 'delete_milestone',
  1257. description: 'Delete a milestone from a repository',
  1258. inputSchema: {
  1259. type: 'object',
  1260. properties: {
  1261. owner: {
  1262. type: 'string',
  1263. description: 'Repository owner username',
  1264. },
  1265. repo: {
  1266. type: 'string',
  1267. description: 'Repository name',
  1268. },
  1269. milestoneId: {
  1270. type: 'number',
  1271. description: 'Milestone ID',
  1272. },
  1273. },
  1274. required: ['owner', 'repo', 'milestoneId'],
  1275. },
  1276. },
  1277. {
  1278. name: 'list_user_organizations',
  1279. description: 'List organizations for the authenticated user',
  1280. inputSchema: {
  1281. type: 'object',
  1282. properties: {},
  1283. },
  1284. },
  1285. {
  1286. name: 'create_organization',
  1287. description: 'Create a new organization',
  1288. inputSchema: {
  1289. type: 'object',
  1290. properties: {
  1291. username: {
  1292. type: 'string',
  1293. description: 'Organization username (alphanumeric, dashes, underscores)',
  1294. },
  1295. full_name: {
  1296. type: 'string',
  1297. description: 'Organization full name',
  1298. },
  1299. description: {
  1300. type: 'string',
  1301. description: 'Organization description',
  1302. },
  1303. website: {
  1304. type: 'string',
  1305. description: 'Organization website',
  1306. },
  1307. location: {
  1308. type: 'string',
  1309. description: 'Organization location',
  1310. },
  1311. },
  1312. required: ['username'],
  1313. },
  1314. },
  1315. {
  1316. name: 'list_public_organizations',
  1317. description: 'List public organizations for a specific user',
  1318. inputSchema: {
  1319. type: 'object',
  1320. properties: {
  1321. username: {
  1322. type: 'string',
  1323. description: 'Username to get public organizations for',
  1324. },
  1325. },
  1326. required: ['username'],
  1327. },
  1328. },
  1329. {
  1330. name: 'get_organization',
  1331. description: 'Get information about a specific organization',
  1332. inputSchema: {
  1333. type: 'object',
  1334. properties: {
  1335. orgname: {
  1336. type: 'string',
  1337. description: 'Organization name',
  1338. },
  1339. },
  1340. required: ['orgname'],
  1341. },
  1342. },
  1343. {
  1344. name: 'update_organization',
  1345. description: 'Update an organization (requires owner permissions)',
  1346. inputSchema: {
  1347. type: 'object',
  1348. properties: {
  1349. orgname: {
  1350. type: 'string',
  1351. description: 'Organization name',
  1352. },
  1353. full_name: {
  1354. type: 'string',
  1355. description: 'Organization full name',
  1356. },
  1357. description: {
  1358. type: 'string',
  1359. description: 'Organization description',
  1360. },
  1361. website: {
  1362. type: 'string',
  1363. description: 'Organization website',
  1364. },
  1365. location: {
  1366. type: 'string',
  1367. description: 'Organization location',
  1368. },
  1369. },
  1370. required: ['orgname'],
  1371. },
  1372. },
  1373. {
  1374. name: 'add_organization_member',
  1375. description: 'Add or update organization membership (requires owner permissions)',
  1376. inputSchema: {
  1377. type: 'object',
  1378. properties: {
  1379. orgname: {
  1380. type: 'string',
  1381. description: 'Organization name',
  1382. },
  1383. username: {
  1384. type: 'string',
  1385. description: 'Username to add as member',
  1386. },
  1387. role: {
  1388. type: 'string',
  1389. enum: ['admin', 'member'],
  1390. description: 'Member role (admin or member)',
  1391. },
  1392. },
  1393. required: ['orgname', 'username', 'role'],
  1394. },
  1395. },
  1396. {
  1397. name: 'list_organization_teams',
  1398. description: 'List teams in an organization',
  1399. inputSchema: {
  1400. type: 'object',
  1401. properties: {
  1402. orgname: {
  1403. type: 'string',
  1404. description: 'Organization name',
  1405. },
  1406. },
  1407. required: ['orgname'],
  1408. },
  1409. },
  1410. {
  1411. name: 'create_team',
  1412. description: 'Create a new team in an organization (requires admin permissions)',
  1413. inputSchema: {
  1414. type: 'object',
  1415. properties: {
  1416. orgname: {
  1417. type: 'string',
  1418. description: 'Organization name',
  1419. },
  1420. name: {
  1421. type: 'string',
  1422. description: 'Team name (max 30 characters, alphanumeric with dashes and dots)',
  1423. },
  1424. description: {
  1425. type: 'string',
  1426. description: 'Team description (max 255 characters)',
  1427. },
  1428. permission: {
  1429. type: 'string',
  1430. enum: ['read', 'write', 'admin'],
  1431. description: 'Team permission level (default: read)',
  1432. },
  1433. },
  1434. required: ['orgname', 'name'],
  1435. },
  1436. },
  1437. {
  1438. name: 'get_team',
  1439. description: 'Get information about a specific team',
  1440. inputSchema: {
  1441. type: 'object',
  1442. properties: {
  1443. teamId: {
  1444. type: 'number',
  1445. description: 'Team ID',
  1446. },
  1447. },
  1448. required: ['teamId'],
  1449. },
  1450. },
  1451. {
  1452. name: 'add_team_member',
  1453. description: 'Add a user to a team (requires admin permissions)',
  1454. inputSchema: {
  1455. type: 'object',
  1456. properties: {
  1457. teamId: {
  1458. type: 'number',
  1459. description: 'Team ID',
  1460. },
  1461. username: {
  1462. type: 'string',
  1463. description: 'Username to add to the team',
  1464. },
  1465. },
  1466. required: ['teamId', 'username'],
  1467. },
  1468. },
  1469. {
  1470. name: 'remove_team_member',
  1471. description: 'Remove a user from a team (requires admin permissions)',
  1472. inputSchema: {
  1473. type: 'object',
  1474. properties: {
  1475. teamId: {
  1476. type: 'number',
  1477. description: 'Team ID',
  1478. },
  1479. username: {
  1480. type: 'string',
  1481. description: 'Username to remove from the team',
  1482. },
  1483. },
  1484. required: ['teamId', 'username'],
  1485. },
  1486. },
  1487. {
  1488. name: 'list_releases',
  1489. description: 'List all releases for a repository',
  1490. inputSchema: {
  1491. type: 'object',
  1492. properties: {
  1493. owner: {
  1494. type: 'string',
  1495. description: 'Repository owner username',
  1496. },
  1497. repo: {
  1498. type: 'string',
  1499. description: 'Repository name',
  1500. },
  1501. },
  1502. required: ['owner', 'repo'],
  1503. },
  1504. },
  1505. {
  1506. name: 'list_collaborators',
  1507. description: 'List all collaborators for a repository',
  1508. inputSchema: {
  1509. type: 'object',
  1510. properties: {
  1511. owner: {
  1512. type: 'string',
  1513. description: 'Repository owner username',
  1514. },
  1515. repo: {
  1516. type: 'string',
  1517. description: 'Repository name',
  1518. },
  1519. },
  1520. required: ['owner', 'repo'],
  1521. },
  1522. },
  1523. {
  1524. name: 'check_collaborator',
  1525. description: 'Check if a user is a collaborator on a repository',
  1526. inputSchema: {
  1527. type: 'object',
  1528. properties: {
  1529. owner: {
  1530. type: 'string',
  1531. description: 'Repository owner username',
  1532. },
  1533. repo: {
  1534. type: 'string',
  1535. description: 'Repository name',
  1536. },
  1537. username: {
  1538. type: 'string',
  1539. description: 'Username to check for collaborator status',
  1540. },
  1541. },
  1542. required: ['owner', 'repo', 'username'],
  1543. },
  1544. },
  1545. {
  1546. name: 'add_collaborator',
  1547. description: 'Add a collaborator to a repository',
  1548. inputSchema: {
  1549. type: 'object',
  1550. properties: {
  1551. owner: {
  1552. type: 'string',
  1553. description: 'Repository owner username',
  1554. },
  1555. repo: {
  1556. type: 'string',
  1557. description: 'Repository name',
  1558. },
  1559. username: {
  1560. type: 'string',
  1561. description: 'Username to add as collaborator',
  1562. },
  1563. permission: {
  1564. type: 'string',
  1565. enum: ['read', 'write', 'admin'],
  1566. description: 'Permission level (default: write)',
  1567. },
  1568. },
  1569. required: ['owner', 'repo', 'username'],
  1570. },
  1571. },
  1572. {
  1573. name: 'remove_collaborator',
  1574. description: 'Remove a collaborator from a repository',
  1575. inputSchema: {
  1576. type: 'object',
  1577. properties: {
  1578. owner: {
  1579. type: 'string',
  1580. description: 'Repository owner username',
  1581. },
  1582. repo: {
  1583. type: 'string',
  1584. description: 'Repository name',
  1585. },
  1586. username: {
  1587. type: 'string',
  1588. description: 'Username to remove as collaborator',
  1589. },
  1590. },
  1591. required: ['owner', 'repo', 'username'],
  1592. },
  1593. },
  1594. {
  1595. name: 'list_user_emails',
  1596. description: 'List email addresses for the authenticated user',
  1597. inputSchema: {
  1598. type: 'object',
  1599. properties: {},
  1600. },
  1601. },
  1602. {
  1603. name: 'add_user_emails',
  1604. description: 'Add email address(es) to the authenticated user account',
  1605. inputSchema: {
  1606. type: 'object',
  1607. properties: {
  1608. emails: {
  1609. type: 'array',
  1610. items: {
  1611. type: 'string',
  1612. },
  1613. description: 'Array of email addresses to add',
  1614. },
  1615. },
  1616. required: ['emails'],
  1617. },
  1618. },
  1619. {
  1620. name: 'delete_user_emails',
  1621. description: 'Delete email address(es) from the authenticated user account',
  1622. inputSchema: {
  1623. type: 'object',
  1624. properties: {
  1625. emails: {
  1626. type: 'array',
  1627. items: {
  1628. type: 'string',
  1629. },
  1630. description: 'Array of email addresses to delete',
  1631. },
  1632. },
  1633. required: ['emails'],
  1634. },
  1635. },
  1636. {
  1637. name: 'list_user_keys',
  1638. description: 'List public SSH keys for a specific user',
  1639. inputSchema: {
  1640. type: 'object',
  1641. properties: {
  1642. username: {
  1643. type: 'string',
  1644. description: 'Username to list public keys for',
  1645. },
  1646. },
  1647. required: ['username'],
  1648. },
  1649. },
  1650. {
  1651. name: 'list_my_keys',
  1652. description: 'List public SSH keys for the authenticated user',
  1653. inputSchema: {
  1654. type: 'object',
  1655. properties: {},
  1656. },
  1657. },
  1658. {
  1659. name: 'get_public_key',
  1660. description: 'Get details of a specific public SSH key by ID',
  1661. inputSchema: {
  1662. type: 'object',
  1663. properties: {
  1664. keyId: {
  1665. type: 'number',
  1666. description: 'Public key ID',
  1667. },
  1668. },
  1669. required: ['keyId'],
  1670. },
  1671. },
  1672. {
  1673. name: 'create_public_key',
  1674. description: 'Create a new public SSH key for the authenticated user',
  1675. inputSchema: {
  1676. type: 'object',
  1677. properties: {
  1678. title: {
  1679. type: 'string',
  1680. description: 'Key title/name for identification',
  1681. },
  1682. key: {
  1683. type: 'string',
  1684. description: 'The public key content (SSH public key format)',
  1685. },
  1686. },
  1687. required: ['title', 'key'],
  1688. },
  1689. },
  1690. {
  1691. name: 'delete_public_key',
  1692. description: 'Delete a public SSH key by ID',
  1693. inputSchema: {
  1694. type: 'object',
  1695. properties: {
  1696. keyId: {
  1697. type: 'number',
  1698. description: 'Public key ID to delete',
  1699. },
  1700. },
  1701. required: ['keyId'],
  1702. },
  1703. },
  1704. {
  1705. name: 'list_hooks',
  1706. description: 'List all webhooks for a repository',
  1707. inputSchema: {
  1708. type: 'object',
  1709. properties: {
  1710. owner: {
  1711. type: 'string',
  1712. description: 'Repository owner username',
  1713. },
  1714. repo: {
  1715. type: 'string',
  1716. description: 'Repository name',
  1717. },
  1718. },
  1719. required: ['owner', 'repo'],
  1720. },
  1721. },
  1722. {
  1723. name: 'create_hook',
  1724. description: 'Create a new webhook for a repository',
  1725. inputSchema: {
  1726. type: 'object',
  1727. properties: {
  1728. owner: {
  1729. type: 'string',
  1730. description: 'Repository owner username',
  1731. },
  1732. repo: {
  1733. type: 'string',
  1734. description: 'Repository name',
  1735. },
  1736. type: {
  1737. type: 'string',
  1738. enum: ['gogs', 'slack'],
  1739. description: 'Webhook type (gogs or slack)',
  1740. },
  1741. config: {
  1742. type: 'object',
  1743. properties: {
  1744. url: {
  1745. type: 'string',
  1746. description: 'URL to send webhook payloads to',
  1747. },
  1748. content_type: {
  1749. type: 'string',
  1750. enum: ['json', 'form'],
  1751. description: 'Content type for payload (json or form)',
  1752. },
  1753. secret: {
  1754. type: 'string',
  1755. description: 'Optional secret for webhook validation',
  1756. },
  1757. channel: {
  1758. type: 'string',
  1759. description: 'Slack channel (for slack webhooks)',
  1760. },
  1761. username: {
  1762. type: 'string',
  1763. description: 'Slack username (for slack webhooks)',
  1764. },
  1765. icon_url: {
  1766. type: 'string',
  1767. description: 'Slack icon URL (for slack webhooks)',
  1768. },
  1769. color: {
  1770. type: 'string',
  1771. description: 'Slack color (for slack webhooks)',
  1772. },
  1773. },
  1774. required: ['url', 'content_type'],
  1775. description: 'Webhook configuration',
  1776. },
  1777. events: {
  1778. type: 'array',
  1779. items: {
  1780. type: 'string',
  1781. enum: ['create', 'delete', 'fork', 'push', 'issues', 'issue_comment', 'pull_request', 'release'],
  1782. },
  1783. description: 'Events that trigger the webhook (default: ["push"])',
  1784. },
  1785. active: {
  1786. type: 'boolean',
  1787. description: 'Whether the webhook is active (default: false)',
  1788. },
  1789. },
  1790. required: ['owner', 'repo', 'type', 'config'],
  1791. },
  1792. },
  1793. {
  1794. name: 'update_hook',
  1795. description: 'Update an existing webhook',
  1796. inputSchema: {
  1797. type: 'object',
  1798. properties: {
  1799. owner: {
  1800. type: 'string',
  1801. description: 'Repository owner username',
  1802. },
  1803. repo: {
  1804. type: 'string',
  1805. description: 'Repository name',
  1806. },
  1807. hookId: {
  1808. type: 'number',
  1809. description: 'Webhook ID to update',
  1810. },
  1811. config: {
  1812. type: 'object',
  1813. properties: {
  1814. url: {
  1815. type: 'string',
  1816. description: 'URL to send webhook payloads to',
  1817. },
  1818. content_type: {
  1819. type: 'string',
  1820. enum: ['json', 'form'],
  1821. description: 'Content type for payload (json or form)',
  1822. },
  1823. secret: {
  1824. type: 'string',
  1825. description: 'Optional secret for webhook validation',
  1826. },
  1827. channel: {
  1828. type: 'string',
  1829. description: 'Slack channel (for slack webhooks)',
  1830. },
  1831. username: {
  1832. type: 'string',
  1833. description: 'Slack username (for slack webhooks)',
  1834. },
  1835. icon_url: {
  1836. type: 'string',
  1837. description: 'Slack icon URL (for slack webhooks)',
  1838. },
  1839. color: {
  1840. type: 'string',
  1841. description: 'Slack color (for slack webhooks)',
  1842. },
  1843. },
  1844. required: ['url', 'content_type'],
  1845. description: 'Webhook configuration',
  1846. },
  1847. events: {
  1848. type: 'array',
  1849. items: {
  1850. type: 'string',
  1851. enum: ['create', 'delete', 'fork', 'push', 'issues', 'issue_comment', 'pull_request', 'release'],
  1852. },
  1853. description: 'Events that trigger the webhook',
  1854. },
  1855. active: {
  1856. type: 'boolean',
  1857. description: 'Whether the webhook is active',
  1858. },
  1859. },
  1860. required: ['owner', 'repo', 'hookId', 'config'],
  1861. },
  1862. },
  1863. {
  1864. name: 'delete_hook',
  1865. description: 'Delete a webhook from a repository',
  1866. inputSchema: {
  1867. type: 'object',
  1868. properties: {
  1869. owner: {
  1870. type: 'string',
  1871. description: 'Repository owner username',
  1872. },
  1873. repo: {
  1874. type: 'string',
  1875. description: 'Repository name',
  1876. },
  1877. hookId: {
  1878. type: 'number',
  1879. description: 'Webhook ID to delete',
  1880. },
  1881. },
  1882. required: ['owner', 'repo', 'hookId'],
  1883. },
  1884. },
  1885. {
  1886. name: 'list_followers',
  1887. description: 'List followers of a specific user',
  1888. inputSchema: {
  1889. type: 'object',
  1890. properties: {
  1891. username: {
  1892. type: 'string',
  1893. description: 'Username to list followers for',
  1894. },
  1895. },
  1896. required: ['username'],
  1897. },
  1898. },
  1899. {
  1900. name: 'list_following',
  1901. description: 'List users that a specific user is following',
  1902. inputSchema: {
  1903. type: 'object',
  1904. properties: {
  1905. username: {
  1906. type: 'string',
  1907. description: 'Username to list following for',
  1908. },
  1909. },
  1910. required: ['username'],
  1911. },
  1912. },
  1913. {
  1914. name: 'check_following',
  1915. description: 'Check if the authenticated user is following a target user',
  1916. inputSchema: {
  1917. type: 'object',
  1918. properties: {
  1919. username: {
  1920. type: 'string',
  1921. description: 'Username to check if authenticated user is following',
  1922. },
  1923. },
  1924. required: ['username'],
  1925. },
  1926. },
  1927. {
  1928. name: 'follow_user',
  1929. description: 'Follow a user (authenticated user follows the target user)',
  1930. inputSchema: {
  1931. type: 'object',
  1932. properties: {
  1933. username: {
  1934. type: 'string',
  1935. description: 'Username to follow',
  1936. },
  1937. },
  1938. required: ['username'],
  1939. },
  1940. },
  1941. {
  1942. name: 'unfollow_user',
  1943. description: 'Unfollow a user (authenticated user unfollows the target user)',
  1944. inputSchema: {
  1945. type: 'object',
  1946. properties: {
  1947. username: {
  1948. type: 'string',
  1949. description: 'Username to unfollow',
  1950. },
  1951. },
  1952. required: ['username'],
  1953. },
  1954. },
  1955. {
  1956. name: 'list_deploy_keys',
  1957. description: 'List all deploy keys for a repository',
  1958. inputSchema: {
  1959. type: 'object',
  1960. properties: {
  1961. owner: {
  1962. type: 'string',
  1963. description: 'Repository owner username',
  1964. },
  1965. repo: {
  1966. type: 'string',
  1967. description: 'Repository name',
  1968. },
  1969. },
  1970. required: ['owner', 'repo'],
  1971. },
  1972. },
  1973. {
  1974. name: 'get_deploy_key',
  1975. description: 'Get details of a specific deploy key',
  1976. inputSchema: {
  1977. type: 'object',
  1978. properties: {
  1979. owner: {
  1980. type: 'string',
  1981. description: 'Repository owner username',
  1982. },
  1983. repo: {
  1984. type: 'string',
  1985. description: 'Repository name',
  1986. },
  1987. keyId: {
  1988. type: 'number',
  1989. description: 'Deploy key ID',
  1990. },
  1991. },
  1992. required: ['owner', 'repo', 'keyId'],
  1993. },
  1994. },
  1995. {
  1996. name: 'create_deploy_key',
  1997. description: 'Add a new deploy key to a repository',
  1998. inputSchema: {
  1999. type: 'object',
  2000. properties: {
  2001. owner: {
  2002. type: 'string',
  2003. description: 'Repository owner username',
  2004. },
  2005. repo: {
  2006. type: 'string',
  2007. description: 'Repository name',
  2008. },
  2009. title: {
  2010. type: 'string',
  2011. description: 'Deploy key title/name for identification',
  2012. },
  2013. key: {
  2014. type: 'string',
  2015. description: 'The public key content (SSH public key format)',
  2016. },
  2017. read_only: {
  2018. type: 'boolean',
  2019. description: 'Whether the key is read-only (default: true)',
  2020. },
  2021. },
  2022. required: ['owner', 'repo', 'title', 'key'],
  2023. },
  2024. },
  2025. {
  2026. name: 'delete_deploy_key',
  2027. description: 'Remove a deploy key from a repository',
  2028. inputSchema: {
  2029. type: 'object',
  2030. properties: {
  2031. owner: {
  2032. type: 'string',
  2033. description: 'Repository owner username',
  2034. },
  2035. repo: {
  2036. type: 'string',
  2037. description: 'Repository name',
  2038. },
  2039. keyId: {
  2040. type: 'number',
  2041. description: 'Deploy key ID to delete',
  2042. },
  2043. },
  2044. required: ['owner', 'repo', 'keyId'],
  2045. },
  2046. },
  2047. {
  2048. name: 'render_markdown',
  2049. description: 'Render markdown text to HTML with support for GitHub Flavored Markdown (GFM) and repository context',
  2050. inputSchema: {
  2051. type: 'object',
  2052. properties: {
  2053. text: {
  2054. type: 'string',
  2055. description: 'Markdown text to render',
  2056. },
  2057. mode: {
  2058. type: 'string',
  2059. enum: ['gfm', 'markdown'],
  2060. description: 'Rendering mode: "gfm" for GitHub Flavored Markdown (default) or "markdown" for standard Markdown',
  2061. },
  2062. context: {
  2063. type: 'string',
  2064. description: 'Repository context in the format "owner/repo" for repository-aware rendering (e.g., for issue references)',
  2065. },
  2066. },
  2067. required: ['text'],
  2068. },
  2069. },
  2070. {
  2071. name: 'render_markdown_raw',
  2072. description: 'Render raw markdown text to HTML (simpler endpoint, plain text input)',
  2073. inputSchema: {
  2074. type: 'object',
  2075. properties: {
  2076. text: {
  2077. type: 'string',
  2078. description: 'Raw markdown text to render to HTML',
  2079. },
  2080. },
  2081. required: ['text'],
  2082. },
  2083. },
  2084. ],
  2085. };
  2086. });
  2087. // Handle tool calls
  2088. server.setRequestHandler(CallToolRequestSchema, async (request) => {
  2089. try {
  2090. const { name, arguments: args } = request.params;
  2091. switch (name) {
  2092. case 'get_current_user': {
  2093. const user = await gogsClient.getCurrentUser();
  2094. return {
  2095. content: [
  2096. {
  2097. type: 'text',
  2098. text: JSON.stringify(user, null, 2),
  2099. },
  2100. ],
  2101. };
  2102. }
  2103. case 'get_user': {
  2104. const { username } = GetUserSchema.parse(args);
  2105. const user = await gogsClient.getUser(username);
  2106. return {
  2107. content: [
  2108. {
  2109. type: 'text',
  2110. text: JSON.stringify(user, null, 2),
  2111. },
  2112. ],
  2113. };
  2114. }
  2115. case 'search_users': {
  2116. const { query, limit } = SearchUsersSchema.parse(args);
  2117. const users = await gogsClient.searchUsers(query, limit);
  2118. return {
  2119. content: [
  2120. {
  2121. type: 'text',
  2122. text: JSON.stringify(users, null, 2),
  2123. },
  2124. ],
  2125. };
  2126. }
  2127. case 'list_user_repositories': {
  2128. const { username } = ListUserReposSchema.parse(args);
  2129. const repos = username
  2130. ? await gogsClient.listUserRepositories(username)
  2131. : await gogsClient.listMyRepositories();
  2132. return {
  2133. content: [
  2134. {
  2135. type: 'text',
  2136. text: JSON.stringify(repos, null, 2),
  2137. },
  2138. ],
  2139. };
  2140. }
  2141. case 'search_repositories': {
  2142. const { query, uid, limit, page } = SearchReposSchema.parse(args);
  2143. const repos = await gogsClient.searchRepositories(query, { uid, limit, page });
  2144. return {
  2145. content: [
  2146. {
  2147. type: 'text',
  2148. text: JSON.stringify(repos, null, 2),
  2149. },
  2150. ],
  2151. };
  2152. }
  2153. case 'get_repository': {
  2154. const { owner, repo } = GetRepoSchema.parse(args);
  2155. const repository = await gogsClient.getRepository(owner, repo);
  2156. return {
  2157. content: [
  2158. {
  2159. type: 'text',
  2160. text: JSON.stringify(repository, null, 2),
  2161. },
  2162. ],
  2163. };
  2164. }
  2165. case 'create_repository': {
  2166. const data = CreateRepoSchema.parse(args);
  2167. const repo = await gogsClient.createRepository(data);
  2168. return {
  2169. content: [
  2170. {
  2171. type: 'text',
  2172. text: JSON.stringify(repo, null, 2),
  2173. },
  2174. ],
  2175. };
  2176. }
  2177. case 'delete_repository': {
  2178. const { owner, repo } = DeleteRepoSchema.parse(args);
  2179. await gogsClient.deleteRepository(owner, repo);
  2180. return {
  2181. content: [
  2182. {
  2183. type: 'text',
  2184. text: `Repository ${owner}/${repo} deleted successfully`,
  2185. },
  2186. ],
  2187. };
  2188. }
  2189. case 'get_contents': {
  2190. const { owner, repo, path, ref } = GetContentsSchema.parse(args);
  2191. const contents = await gogsClient.getContents(owner, repo, path, ref);
  2192. // If it's a file with base64 content, decode it
  2193. if (!Array.isArray(contents) && contents.type === 'file' && contents.content) {
  2194. const decodedContent = Buffer.from(contents.content, 'base64').toString('utf-8');
  2195. return {
  2196. content: [
  2197. {
  2198. type: 'text',
  2199. text: `File: ${contents.path}\nSize: ${contents.size} bytes\nSHA: ${contents.sha}\n\nContent:\n${decodedContent}`,
  2200. },
  2201. ],
  2202. };
  2203. }
  2204. return {
  2205. content: [
  2206. {
  2207. type: 'text',
  2208. text: JSON.stringify(contents, null, 2),
  2209. },
  2210. ],
  2211. };
  2212. }
  2213. case 'get_raw_content': {
  2214. const { owner, repo, ref, path } = GetRawContentSchema.parse(args);
  2215. const content = await gogsClient.getRawContent(owner, repo, ref, path);
  2216. return {
  2217. content: [
  2218. {
  2219. type: 'text',
  2220. text: content,
  2221. },
  2222. ],
  2223. };
  2224. }
  2225. case 'list_branches': {
  2226. const { owner, repo } = ListBranchesSchema.parse(args);
  2227. const branches = await gogsClient.listBranches(owner, repo);
  2228. return {
  2229. content: [
  2230. {
  2231. type: 'text',
  2232. text: JSON.stringify(branches, null, 2),
  2233. },
  2234. ],
  2235. };
  2236. }
  2237. case 'get_commits': {
  2238. const { owner, repo, sha, page } = GetCommitsSchema.parse(args);
  2239. const commits = await gogsClient.getCommits(owner, repo, { sha, page });
  2240. return {
  2241. content: [
  2242. {
  2243. type: 'text',
  2244. text: JSON.stringify(commits, null, 2),
  2245. },
  2246. ],
  2247. };
  2248. }
  2249. case 'get_tree': {
  2250. const { owner, repo, sha } = GetTreeSchema.parse(args);
  2251. const tree = await gogsClient.getTree(owner, repo, sha);
  2252. return {
  2253. content: [
  2254. {
  2255. type: 'text',
  2256. text: JSON.stringify(tree, null, 2),
  2257. },
  2258. ],
  2259. };
  2260. }
  2261. case 'list_issues': {
  2262. const { owner, repo, state, labels, page, per_page } = ListIssuesSchema.parse(args);
  2263. const issues = await gogsClient.listIssues(owner, repo, { state, labels, page, per_page });
  2264. return {
  2265. content: [
  2266. {
  2267. type: 'text',
  2268. text: JSON.stringify(issues, null, 2),
  2269. },
  2270. ],
  2271. };
  2272. }
  2273. case 'get_issue': {
  2274. const { owner, repo, number } = GetIssueSchema.parse(args);
  2275. const issue = await gogsClient.getIssue(owner, repo, number);
  2276. return {
  2277. content: [
  2278. {
  2279. type: 'text',
  2280. text: JSON.stringify(issue, null, 2),
  2281. },
  2282. ],
  2283. };
  2284. }
  2285. case 'create_issue': {
  2286. const { owner, repo, title, body, assignee, milestone, labels } = CreateIssueSchema.parse(args);
  2287. const issue = await gogsClient.createIssue(owner, repo, {
  2288. title,
  2289. body,
  2290. assignee,
  2291. milestone,
  2292. labels,
  2293. });
  2294. return {
  2295. content: [
  2296. {
  2297. type: 'text',
  2298. text: JSON.stringify(issue, null, 2),
  2299. },
  2300. ],
  2301. };
  2302. }
  2303. case 'update_issue': {
  2304. const { owner, repo, number, title, body, assignee, milestone, state, labels } = UpdateIssueSchema.parse(args);
  2305. const issue = await gogsClient.updateIssue(owner, repo, number, {
  2306. title,
  2307. body,
  2308. assignee,
  2309. milestone,
  2310. state,
  2311. labels,
  2312. });
  2313. return {
  2314. content: [
  2315. {
  2316. type: 'text',
  2317. text: JSON.stringify(issue, null, 2),
  2318. },
  2319. ],
  2320. };
  2321. }
  2322. case 'list_issue_comments': {
  2323. const { owner, repo, number } = ListIssueCommentsSchema.parse(args);
  2324. const comments = await gogsClient.listIssueComments(owner, repo, number);
  2325. return {
  2326. content: [
  2327. {
  2328. type: 'text',
  2329. text: JSON.stringify(comments, null, 2),
  2330. },
  2331. ],
  2332. };
  2333. }
  2334. case 'create_issue_comment': {
  2335. const { owner, repo, number, body } = CreateIssueCommentSchema.parse(args);
  2336. const comment = await gogsClient.createIssueComment(owner, repo, number, body);
  2337. return {
  2338. content: [
  2339. {
  2340. type: 'text',
  2341. text: JSON.stringify(comment, null, 2),
  2342. },
  2343. ],
  2344. };
  2345. }
  2346. case 'update_issue_comment': {
  2347. const { owner, repo, commentId, body } = UpdateIssueCommentSchema.parse(args);
  2348. const comment = await gogsClient.updateIssueComment(owner, repo, commentId, body);
  2349. return {
  2350. content: [
  2351. {
  2352. type: 'text',
  2353. text: JSON.stringify(comment, null, 2),
  2354. },
  2355. ],
  2356. };
  2357. }
  2358. case 'list_labels': {
  2359. const { owner, repo } = ListLabelsSchema.parse(args);
  2360. const labels = await gogsClient.listLabels(owner, repo);
  2361. return {
  2362. content: [
  2363. {
  2364. type: 'text',
  2365. text: JSON.stringify(labels, null, 2),
  2366. },
  2367. ],
  2368. };
  2369. }
  2370. case 'get_label': {
  2371. const { owner, repo, labelId } = GetLabelSchema.parse(args);
  2372. const label = await gogsClient.getLabel(owner, repo, labelId);
  2373. return {
  2374. content: [
  2375. {
  2376. type: 'text',
  2377. text: JSON.stringify(label, null, 2),
  2378. },
  2379. ],
  2380. };
  2381. }
  2382. case 'create_label': {
  2383. const { owner, repo, name, color } = CreateLabelSchema.parse(args);
  2384. const label = await gogsClient.createLabel(owner, repo, { name, color });
  2385. return {
  2386. content: [
  2387. {
  2388. type: 'text',
  2389. text: JSON.stringify(label, null, 2),
  2390. },
  2391. ],
  2392. };
  2393. }
  2394. case 'update_label': {
  2395. const { owner, repo, labelId, name, color } = UpdateLabelSchema.parse(args);
  2396. const label = await gogsClient.updateLabel(owner, repo, labelId, { name, color });
  2397. return {
  2398. content: [
  2399. {
  2400. type: 'text',
  2401. text: JSON.stringify(label, null, 2),
  2402. },
  2403. ],
  2404. };
  2405. }
  2406. case 'delete_label': {
  2407. const { owner, repo, labelId } = DeleteLabelSchema.parse(args);
  2408. await gogsClient.deleteLabel(owner, repo, labelId);
  2409. return {
  2410. content: [
  2411. {
  2412. type: 'text',
  2413. text: `Label ${labelId} deleted successfully from ${owner}/${repo}`,
  2414. },
  2415. ],
  2416. };
  2417. }
  2418. case 'list_issue_labels': {
  2419. const { owner, repo, number } = ListIssueLabelsSchema.parse(args);
  2420. const labels = await gogsClient.listIssueLabels(owner, repo, number);
  2421. return {
  2422. content: [
  2423. {
  2424. type: 'text',
  2425. text: JSON.stringify(labels, null, 2),
  2426. },
  2427. ],
  2428. };
  2429. }
  2430. case 'add_issue_labels': {
  2431. const { owner, repo, number, labels } = AddIssueLabelsSchema.parse(args);
  2432. const result = await gogsClient.addIssueLabels(owner, repo, number, labels);
  2433. return {
  2434. content: [
  2435. {
  2436. type: 'text',
  2437. text: JSON.stringify(result, null, 2),
  2438. },
  2439. ],
  2440. };
  2441. }
  2442. case 'remove_issue_label': {
  2443. const { owner, repo, number, labelId } = RemoveIssueLabelSchema.parse(args);
  2444. await gogsClient.removeIssueLabel(owner, repo, number, labelId);
  2445. return {
  2446. content: [
  2447. {
  2448. type: 'text',
  2449. text: `Label ${labelId} removed successfully from issue #${number}`,
  2450. },
  2451. ],
  2452. };
  2453. }
  2454. case 'replace_issue_labels': {
  2455. const { owner, repo, number, labels } = ReplaceIssueLabelsSchema.parse(args);
  2456. const result = await gogsClient.replaceIssueLabels(owner, repo, number, labels);
  2457. return {
  2458. content: [
  2459. {
  2460. type: 'text',
  2461. text: JSON.stringify(result, null, 2),
  2462. },
  2463. ],
  2464. };
  2465. }
  2466. case 'remove_all_issue_labels': {
  2467. const { owner, repo, number } = RemoveAllIssueLabelsSchema.parse(args);
  2468. await gogsClient.removeAllIssueLabels(owner, repo, number);
  2469. return {
  2470. content: [
  2471. {
  2472. type: 'text',
  2473. text: `All labels removed successfully from issue #${number}`,
  2474. },
  2475. ],
  2476. };
  2477. }
  2478. case 'list_milestones': {
  2479. const { owner, repo } = ListMilestonesSchema.parse(args);
  2480. const milestones = await gogsClient.listMilestones(owner, repo);
  2481. return {
  2482. content: [
  2483. {
  2484. type: 'text',
  2485. text: JSON.stringify(milestones, null, 2),
  2486. },
  2487. ],
  2488. };
  2489. }
  2490. case 'get_milestone': {
  2491. const { owner, repo, milestoneId } = GetMilestoneSchema.parse(args);
  2492. const milestone = await gogsClient.getMilestone(owner, repo, milestoneId);
  2493. return {
  2494. content: [
  2495. {
  2496. type: 'text',
  2497. text: JSON.stringify(milestone, null, 2),
  2498. },
  2499. ],
  2500. };
  2501. }
  2502. case 'create_milestone': {
  2503. const { owner, repo, title, description, due_on } = CreateMilestoneSchema.parse(args);
  2504. const milestone = await gogsClient.createMilestone(owner, repo, {
  2505. title,
  2506. description,
  2507. due_on,
  2508. });
  2509. return {
  2510. content: [
  2511. {
  2512. type: 'text',
  2513. text: JSON.stringify(milestone, null, 2),
  2514. },
  2515. ],
  2516. };
  2517. }
  2518. case 'update_milestone': {
  2519. const { owner, repo, milestoneId, title, description, due_on, state } = UpdateMilestoneSchema.parse(args);
  2520. const milestone = await gogsClient.updateMilestone(owner, repo, milestoneId, {
  2521. title,
  2522. description,
  2523. due_on,
  2524. state,
  2525. });
  2526. return {
  2527. content: [
  2528. {
  2529. type: 'text',
  2530. text: JSON.stringify(milestone, null, 2),
  2531. },
  2532. ],
  2533. };
  2534. }
  2535. case 'delete_milestone': {
  2536. const { owner, repo, milestoneId } = DeleteMilestoneSchema.parse(args);
  2537. await gogsClient.deleteMilestone(owner, repo, milestoneId);
  2538. return {
  2539. content: [
  2540. {
  2541. type: 'text',
  2542. text: `Milestone ${milestoneId} deleted successfully from ${owner}/${repo}`,
  2543. },
  2544. ],
  2545. };
  2546. }
  2547. case 'list_user_organizations': {
  2548. const organizations = await gogsClient.listUserOrganizations();
  2549. return {
  2550. content: [
  2551. {
  2552. type: 'text',
  2553. text: JSON.stringify(organizations, null, 2),
  2554. },
  2555. ],
  2556. };
  2557. }
  2558. case 'create_organization': {
  2559. const { username, full_name, description, website, location } = CreateOrganizationSchema.parse(args);
  2560. const organization = await gogsClient.createOrganization({
  2561. username,
  2562. full_name,
  2563. description,
  2564. website,
  2565. location,
  2566. });
  2567. return {
  2568. content: [
  2569. {
  2570. type: 'text',
  2571. text: JSON.stringify(organization, null, 2),
  2572. },
  2573. ],
  2574. };
  2575. }
  2576. case 'list_public_organizations': {
  2577. const { username } = ListPublicOrganizationsSchema.parse(args);
  2578. const organizations = await gogsClient.listPublicOrganizations(username);
  2579. return {
  2580. content: [
  2581. {
  2582. type: 'text',
  2583. text: JSON.stringify(organizations, null, 2),
  2584. },
  2585. ],
  2586. };
  2587. }
  2588. case 'get_organization': {
  2589. const { orgname } = GetOrganizationSchema.parse(args);
  2590. const organization = await gogsClient.getOrganization(orgname);
  2591. return {
  2592. content: [
  2593. {
  2594. type: 'text',
  2595. text: JSON.stringify(organization, null, 2),
  2596. },
  2597. ],
  2598. };
  2599. }
  2600. case 'update_organization': {
  2601. const { orgname, full_name, description, website, location } = UpdateOrganizationSchema.parse(args);
  2602. const organization = await gogsClient.updateOrganization(orgname, {
  2603. full_name,
  2604. description,
  2605. website,
  2606. location,
  2607. });
  2608. return {
  2609. content: [
  2610. {
  2611. type: 'text',
  2612. text: JSON.stringify(organization, null, 2),
  2613. },
  2614. ],
  2615. };
  2616. }
  2617. case 'add_organization_member': {
  2618. const { orgname, username, role } = AddOrganizationMemberSchema.parse(args);
  2619. await gogsClient.addOrganizationMember(orgname, username, role);
  2620. return {
  2621. content: [
  2622. {
  2623. type: 'text',
  2624. text: `User ${username} added to organization ${orgname} with role ${role}`,
  2625. },
  2626. ],
  2627. };
  2628. }
  2629. case 'list_organization_teams': {
  2630. const { orgname } = ListOrganizationTeamsSchema.parse(args);
  2631. const teams = await gogsClient.listOrganizationTeams(orgname);
  2632. return {
  2633. content: [
  2634. {
  2635. type: 'text',
  2636. text: JSON.stringify(teams, null, 2),
  2637. },
  2638. ],
  2639. };
  2640. }
  2641. case 'create_team': {
  2642. const { orgname, name, description, permission } = CreateTeamSchema.parse(args);
  2643. const team = await gogsClient.createTeam(orgname, {
  2644. name,
  2645. description,
  2646. permission,
  2647. });
  2648. return {
  2649. content: [
  2650. {
  2651. type: 'text',
  2652. text: JSON.stringify(team, null, 2),
  2653. },
  2654. ],
  2655. };
  2656. }
  2657. case 'get_team': {
  2658. const { teamId } = GetTeamSchema.parse(args);
  2659. const team = await gogsClient.getTeam(teamId);
  2660. return {
  2661. content: [
  2662. {
  2663. type: 'text',
  2664. text: JSON.stringify(team, null, 2),
  2665. },
  2666. ],
  2667. };
  2668. }
  2669. case 'add_team_member': {
  2670. const { teamId, username } = AddTeamMemberSchema.parse(args);
  2671. await gogsClient.addTeamMember(teamId, username);
  2672. return {
  2673. content: [
  2674. {
  2675. type: 'text',
  2676. text: `User ${username} added to team ${teamId} successfully`,
  2677. },
  2678. ],
  2679. };
  2680. }
  2681. case 'remove_team_member': {
  2682. const { teamId, username } = RemoveTeamMemberSchema.parse(args);
  2683. await gogsClient.removeTeamMember(teamId, username);
  2684. return {
  2685. content: [
  2686. {
  2687. type: 'text',
  2688. text: `User ${username} removed from team ${teamId} successfully`,
  2689. },
  2690. ],
  2691. };
  2692. }
  2693. case 'list_releases': {
  2694. const { owner, repo } = ListReleasesSchema.parse(args);
  2695. const releases = await gogsClient.listReleases(owner, repo);
  2696. return {
  2697. content: [
  2698. {
  2699. type: 'text',
  2700. text: JSON.stringify(releases, null, 2),
  2701. },
  2702. ],
  2703. };
  2704. }
  2705. case 'list_collaborators': {
  2706. const { owner, repo } = ListCollaboratorsSchema.parse(args);
  2707. const collaborators = await gogsClient.listCollaborators(owner, repo);
  2708. return {
  2709. content: [
  2710. {
  2711. type: 'text',
  2712. text: JSON.stringify(collaborators, null, 2),
  2713. },
  2714. ],
  2715. };
  2716. }
  2717. case 'check_collaborator': {
  2718. const { owner, repo, username } = CheckCollaboratorSchema.parse(args);
  2719. const isCollaborator = await gogsClient.checkCollaborator(owner, repo, username);
  2720. return {
  2721. content: [
  2722. {
  2723. type: 'text',
  2724. text: JSON.stringify({ isCollaborator, username, repository: `${owner}/${repo}` }, null, 2),
  2725. },
  2726. ],
  2727. };
  2728. }
  2729. case 'add_collaborator': {
  2730. const { owner, repo, username, permission } = AddCollaboratorSchema.parse(args);
  2731. await gogsClient.addCollaborator(owner, repo, username, permission);
  2732. return {
  2733. content: [
  2734. {
  2735. type: 'text',
  2736. text: JSON.stringify({
  2737. success: true,
  2738. message: `Successfully added ${username} as collaborator to ${owner}/${repo}`,
  2739. permission: permission || 'write'
  2740. }, null, 2),
  2741. },
  2742. ],
  2743. };
  2744. }
  2745. case 'remove_collaborator': {
  2746. const { owner, repo, username } = RemoveCollaboratorSchema.parse(args);
  2747. await gogsClient.removeCollaborator(owner, repo, username);
  2748. return {
  2749. content: [
  2750. {
  2751. type: 'text',
  2752. text: JSON.stringify({
  2753. success: true,
  2754. message: `Successfully removed ${username} as collaborator from ${owner}/${repo}`
  2755. }, null, 2),
  2756. },
  2757. ],
  2758. };
  2759. }
  2760. case 'list_user_emails': {
  2761. const emails = await gogsClient.listUserEmails();
  2762. return {
  2763. content: [
  2764. {
  2765. type: 'text',
  2766. text: JSON.stringify(emails, null, 2),
  2767. },
  2768. ],
  2769. };
  2770. }
  2771. case 'add_user_emails': {
  2772. const { emails } = AddUserEmailsSchema.parse(args);
  2773. const addedEmails = await gogsClient.addUserEmails(emails);
  2774. return {
  2775. content: [
  2776. {
  2777. type: 'text',
  2778. text: JSON.stringify(addedEmails, null, 2),
  2779. },
  2780. ],
  2781. };
  2782. }
  2783. case 'delete_user_emails': {
  2784. const { emails } = DeleteUserEmailsSchema.parse(args);
  2785. await gogsClient.deleteUserEmails(emails);
  2786. return {
  2787. content: [
  2788. {
  2789. type: 'text',
  2790. text: JSON.stringify({
  2791. success: true,
  2792. message: `Successfully deleted ${emails.length} email address(es)`,
  2793. emails
  2794. }, null, 2),
  2795. },
  2796. ],
  2797. };
  2798. }
  2799. case 'list_user_keys': {
  2800. const { username } = ListUserKeysSchema.parse(args);
  2801. const keys = await gogsClient.listUserKeys(username);
  2802. return {
  2803. content: [
  2804. {
  2805. type: 'text',
  2806. text: JSON.stringify(keys, null, 2),
  2807. },
  2808. ],
  2809. };
  2810. }
  2811. case 'list_my_keys': {
  2812. const keys = await gogsClient.listMyKeys();
  2813. return {
  2814. content: [
  2815. {
  2816. type: 'text',
  2817. text: JSON.stringify(keys, null, 2),
  2818. },
  2819. ],
  2820. };
  2821. }
  2822. case 'get_public_key': {
  2823. const { keyId } = GetPublicKeySchema.parse(args);
  2824. const key = await gogsClient.getPublicKey(keyId);
  2825. return {
  2826. content: [
  2827. {
  2828. type: 'text',
  2829. text: JSON.stringify(key, null, 2),
  2830. },
  2831. ],
  2832. };
  2833. }
  2834. case 'create_public_key': {
  2835. const { title, key } = CreatePublicKeySchema.parse(args);
  2836. const newKey = await gogsClient.createPublicKey({ title, key });
  2837. return {
  2838. content: [
  2839. {
  2840. type: 'text',
  2841. text: JSON.stringify(newKey, null, 2),
  2842. },
  2843. ],
  2844. };
  2845. }
  2846. case 'delete_public_key': {
  2847. const { keyId } = DeletePublicKeySchema.parse(args);
  2848. await gogsClient.deletePublicKey(keyId);
  2849. return {
  2850. content: [
  2851. {
  2852. type: 'text',
  2853. text: JSON.stringify({
  2854. success: true,
  2855. message: `Successfully deleted public key with ID ${keyId}`,
  2856. keyId
  2857. }, null, 2),
  2858. },
  2859. ],
  2860. };
  2861. }
  2862. case 'list_hooks': {
  2863. const { owner, repo } = ListHooksSchema.parse(args);
  2864. const hooks = await gogsClient.listHooks(owner, repo);
  2865. return {
  2866. content: [
  2867. {
  2868. type: 'text',
  2869. text: JSON.stringify(hooks, null, 2),
  2870. },
  2871. ],
  2872. };
  2873. }
  2874. case 'create_hook': {
  2875. const { owner, repo, type, config, events, active } = CreateHookSchema.parse(args);
  2876. const hook = await gogsClient.createHook(owner, repo, {
  2877. type,
  2878. config,
  2879. events,
  2880. active,
  2881. });
  2882. return {
  2883. content: [
  2884. {
  2885. type: 'text',
  2886. text: JSON.stringify(hook, null, 2),
  2887. },
  2888. ],
  2889. };
  2890. }
  2891. case 'update_hook': {
  2892. const { owner, repo, hookId, config, events, active } = UpdateHookSchema.parse(args);
  2893. const hook = await gogsClient.updateHook(owner, repo, hookId, {
  2894. config,
  2895. events,
  2896. active,
  2897. });
  2898. return {
  2899. content: [
  2900. {
  2901. type: 'text',
  2902. text: JSON.stringify(hook, null, 2),
  2903. },
  2904. ],
  2905. };
  2906. }
  2907. case 'delete_hook': {
  2908. const { owner, repo, hookId } = DeleteHookSchema.parse(args);
  2909. await gogsClient.deleteHook(owner, repo, hookId);
  2910. return {
  2911. content: [
  2912. {
  2913. type: 'text',
  2914. text: JSON.stringify({
  2915. success: true,
  2916. message: `Successfully deleted webhook ${hookId} from ${owner}/${repo}`,
  2917. hookId,
  2918. repository: `${owner}/${repo}`
  2919. }, null, 2),
  2920. },
  2921. ],
  2922. };
  2923. }
  2924. case 'list_followers': {
  2925. const { username } = ListFollowersSchema.parse(args);
  2926. const followers = await gogsClient.listFollowers(username);
  2927. return {
  2928. content: [
  2929. {
  2930. type: 'text',
  2931. text: JSON.stringify(followers, null, 2),
  2932. },
  2933. ],
  2934. };
  2935. }
  2936. case 'list_following': {
  2937. const { username } = ListFollowingSchema.parse(args);
  2938. const following = await gogsClient.listFollowing(username);
  2939. return {
  2940. content: [
  2941. {
  2942. type: 'text',
  2943. text: JSON.stringify(following, null, 2),
  2944. },
  2945. ],
  2946. };
  2947. }
  2948. case 'check_following': {
  2949. const { username } = CheckFollowingSchema.parse(args);
  2950. const isFollowing = await gogsClient.checkFollowing(username);
  2951. return {
  2952. content: [
  2953. {
  2954. type: 'text',
  2955. text: JSON.stringify({
  2956. isFollowing,
  2957. username,
  2958. message: isFollowing
  2959. ? `You are following ${username}`
  2960. : `You are not following ${username}`
  2961. }, null, 2),
  2962. },
  2963. ],
  2964. };
  2965. }
  2966. case 'follow_user': {
  2967. const { username } = FollowUserSchema.parse(args);
  2968. await gogsClient.followUser(username);
  2969. return {
  2970. content: [
  2971. {
  2972. type: 'text',
  2973. text: JSON.stringify({
  2974. success: true,
  2975. message: `Successfully followed user ${username}`,
  2976. username
  2977. }, null, 2),
  2978. },
  2979. ],
  2980. };
  2981. }
  2982. case 'unfollow_user': {
  2983. const { username } = UnfollowUserSchema.parse(args);
  2984. await gogsClient.unfollowUser(username);
  2985. return {
  2986. content: [
  2987. {
  2988. type: 'text',
  2989. text: JSON.stringify({
  2990. success: true,
  2991. message: `Successfully unfollowed user ${username}`,
  2992. username
  2993. }, null, 2),
  2994. },
  2995. ],
  2996. };
  2997. }
  2998. case 'list_deploy_keys': {
  2999. const { owner, repo } = ListDeployKeysSchema.parse(args);
  3000. const keys = await gogsClient.listDeployKeys(owner, repo);
  3001. return {
  3002. content: [
  3003. {
  3004. type: 'text',
  3005. text: JSON.stringify(keys, null, 2),
  3006. },
  3007. ],
  3008. };
  3009. }
  3010. case 'get_deploy_key': {
  3011. const { owner, repo, keyId } = GetDeployKeySchema.parse(args);
  3012. const key = await gogsClient.getDeployKey(owner, repo, keyId);
  3013. return {
  3014. content: [
  3015. {
  3016. type: 'text',
  3017. text: JSON.stringify(key, null, 2),
  3018. },
  3019. ],
  3020. };
  3021. }
  3022. case 'create_deploy_key': {
  3023. const { owner, repo, title, key, read_only } = CreateDeployKeySchema.parse(args);
  3024. const deployKey = await gogsClient.createDeployKey(owner, repo, {
  3025. title,
  3026. key,
  3027. read_only,
  3028. });
  3029. return {
  3030. content: [
  3031. {
  3032. type: 'text',
  3033. text: JSON.stringify({
  3034. success: true,
  3035. message: 'Deploy key created successfully',
  3036. key: deployKey
  3037. }, null, 2),
  3038. },
  3039. ],
  3040. };
  3041. }
  3042. case 'delete_deploy_key': {
  3043. const { owner, repo, keyId } = DeleteDeployKeySchema.parse(args);
  3044. await gogsClient.deleteDeployKey(owner, repo, keyId);
  3045. return {
  3046. content: [
  3047. {
  3048. type: 'text',
  3049. text: JSON.stringify({
  3050. success: true,
  3051. message: `Successfully deleted deploy key ${keyId}`,
  3052. keyId
  3053. }, null, 2),
  3054. },
  3055. ],
  3056. };
  3057. }
  3058. case 'render_markdown': {
  3059. const { text, mode, context } = RenderMarkdownSchema.parse(args);
  3060. const html = await gogsClient.renderMarkdown({
  3061. text,
  3062. mode,
  3063. context,
  3064. });
  3065. return {
  3066. content: [
  3067. {
  3068. type: 'text',
  3069. text: html,
  3070. },
  3071. ],
  3072. };
  3073. }
  3074. case 'render_markdown_raw': {
  3075. const { text } = RenderMarkdownRawSchema.parse(args);
  3076. const html = await gogsClient.renderMarkdownRaw(text);
  3077. return {
  3078. content: [
  3079. {
  3080. type: 'text',
  3081. text: html,
  3082. },
  3083. ],
  3084. };
  3085. }
  3086. default:
  3087. throw new Error(`Unknown tool: ${name}`);
  3088. }
  3089. } catch (error) {
  3090. if (error instanceof Error) {
  3091. return {
  3092. content: [
  3093. {
  3094. type: 'text',
  3095. text: `Error: ${error.message}`,
  3096. },
  3097. ],
  3098. isError: true,
  3099. };
  3100. }
  3101. throw error;
  3102. }
  3103. });
  3104. // Handle resources (optional - for browsing repository structure)
  3105. server.setRequestHandler(ListResourcesRequestSchema, async () => {
  3106. return {
  3107. resources: [],
  3108. };
  3109. });
  3110. server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
  3111. throw new Error('Resource reading not yet implemented');
  3112. });
  3113. return server;
  3114. }