server.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // Package server builds the MCP server and registers the WikiJS tools.
  2. package server
  3. import (
  4. "log/slog"
  5. "gogs.netdome.biz/paul/go-wikijs-mcp/internal/wikijs"
  6. "github.com/modelcontextprotocol/go-sdk/mcp"
  7. )
  8. const instructions = `This server exposes a WikiJS knowledge base.
  9. Use search_pages to find pages by full-text query, get_page_by_id or
  10. get_page_by_path to read a page, and get_all_pages to browse the wiki.
  11. Page results include absolute URLs that can be shared with the user.
  12. Assets are managed store-wide, not per page: list_asset_folders and
  13. list_assets browse the central asset store, and when the write tools are
  14. enabled upload_asset adds files and create_asset_folder adds folders.
  15. Reference uploaded assets from pages with the returned markdown snippet.`
  16. // Options configures the MCP server.
  17. type Options struct {
  18. // Version is reported to MCP clients.
  19. Version string
  20. // EnableEdit exposes the create_page and edit_page tools.
  21. EnableEdit bool
  22. // Logger receives MCP protocol logs. It must write to stderr when the
  23. // stdio transport is used.
  24. Logger *slog.Logger
  25. }
  26. // New creates an MCP server with all WikiJS tools registered.
  27. func New(client *wikijs.Client, opts Options) *mcp.Server {
  28. logger := opts.Logger
  29. if logger == nil {
  30. logger = slog.New(slog.DiscardHandler)
  31. }
  32. s := mcp.NewServer(&mcp.Implementation{
  33. Name: "wikijs-mcp-server",
  34. Title: "WikiJS MCP Server",
  35. Version: opts.Version,
  36. Description: "Search, read, and optionally edit pages in a WikiJS instance.",
  37. }, &mcp.ServerOptions{
  38. Instructions: instructions,
  39. Logger: logger,
  40. })
  41. h := &handlers{wiki: client}
  42. readOnly := &mcp.ToolAnnotations{ReadOnlyHint: true}
  43. mcp.AddTool(s, &mcp.Tool{
  44. Name: "search_pages",
  45. Title: "Search pages",
  46. Description: "Search for pages in WikiJS by query string.",
  47. Annotations: readOnly,
  48. }, h.searchPages)
  49. mcp.AddTool(s, &mcp.Tool{
  50. Name: "get_page_by_id",
  51. Title: "Get page by ID",
  52. Description: "Get a WikiJS page by its numeric ID.",
  53. Annotations: readOnly,
  54. }, h.pageByID)
  55. mcp.AddTool(s, &mcp.Tool{
  56. Name: "get_page_by_path",
  57. Title: "Get page by path",
  58. Description: "Get a WikiJS page by its path and locale.",
  59. Annotations: readOnly,
  60. }, h.pageByPath)
  61. mcp.AddTool(s, &mcp.Tool{
  62. Name: "get_all_pages",
  63. Title: "List pages",
  64. Description: "List pages in WikiJS, optionally filtered by limit, locale, and tags.",
  65. Annotations: readOnly,
  66. }, h.listPages)
  67. mcp.AddTool(s, &mcp.Tool{
  68. Name: "list_asset_folders",
  69. Title: "List asset folders",
  70. Description: "List the child folders of a WikiJS asset folder (root by default).",
  71. Annotations: readOnly,
  72. }, h.listAssetFolders)
  73. mcp.AddTool(s, &mcp.Tool{
  74. Name: "list_assets",
  75. Title: "List assets",
  76. Description: "List the files uploaded to a WikiJS asset folder (root by default), optionally filtered by kind.",
  77. Annotations: readOnly,
  78. }, h.listAssets)
  79. if opts.EnableEdit {
  80. notDestructive := false
  81. mcp.AddTool(s, &mcp.Tool{
  82. Name: "create_page",
  83. Title: "Create page",
  84. Description: "Create a new WikiJS page.",
  85. Annotations: &mcp.ToolAnnotations{DestructiveHint: &notDestructive},
  86. }, h.createPage)
  87. // edit_page advertises IdempotentHint: editing the same page twice
  88. // with the same content converges to the same state, unlike
  89. // create_page and upload_asset, which would duplicate work.
  90. mcp.AddTool(s, &mcp.Tool{
  91. Name: "edit_page",
  92. Title: "Edit page",
  93. Description: "Edit an existing WikiJS page by its numeric ID.",
  94. Annotations: &mcp.ToolAnnotations{IdempotentHint: true},
  95. }, h.editPage)
  96. mcp.AddTool(s, &mcp.Tool{
  97. Name: "upload_asset",
  98. Title: "Upload asset",
  99. Description: "Upload a file to a WikiJS asset folder so it can be referenced from pages.",
  100. Annotations: &mcp.ToolAnnotations{DestructiveHint: &notDestructive},
  101. }, h.uploadAsset)
  102. mcp.AddTool(s, &mcp.Tool{
  103. Name: "create_asset_folder",
  104. Title: "Create asset folder",
  105. Description: "Create a new WikiJS asset folder.",
  106. Annotations: &mcp.ToolAnnotations{DestructiveHint: &notDestructive},
  107. }, h.createAssetFolder)
  108. }
  109. return s
  110. }