directify-mcp 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +50 -0
  2. package/package.json +1 -1
  3. package/src/tools.js +139 -0
package/README.md CHANGED
@@ -6,6 +6,10 @@
6
6
 
7
7
  This MCP server lets AI assistants directly manage your Directify directories. Ask Claude to create listings, update categories, publish articles, and more - all through natural language.
8
8
 
9
+ You can use it in two ways:
10
+ - **Local** - Install the npm package and run it on your machine (see [Installation](#installation))
11
+ - **Remote** - Use the hosted server with zero installation (see [Remote Server](#remote-server-no-installation-required))
12
+
9
13
  ## Installation
10
14
 
11
15
  ```bash
@@ -62,6 +66,52 @@ Add to `.cursor/mcp.json` in your project:
62
66
  }
63
67
  ```
64
68
 
69
+ ## Remote Server (No Installation Required)
70
+
71
+ If you prefer not to install anything locally, you can use the hosted remote MCP server. This works with any MCP client that supports remote servers via `mcp-remote`.
72
+
73
+ ### Claude Desktop (Remote)
74
+
75
+ ```json
76
+ {
77
+ "mcpServers": {
78
+ "directify": {
79
+ "command": "npx",
80
+ "args": [
81
+ "-y", "mcp-remote",
82
+ "https://mcp.directify.app/mcp",
83
+ "--header", "Authorization:Bearer YOUR_API_TOKEN",
84
+ "--header", "X-Directory-ID:YOUR_DIRECTORY_ID"
85
+ ]
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Cursor (Remote)
92
+
93
+ Add to `.cursor/mcp.json`:
94
+
95
+ ```json
96
+ {
97
+ "mcpServers": {
98
+ "directify": {
99
+ "command": "npx",
100
+ "args": [
101
+ "-y", "mcp-remote",
102
+ "https://mcp.directify.app/mcp",
103
+ "--header", "Authorization:Bearer YOUR_API_TOKEN",
104
+ "--header", "X-Directory-ID:YOUR_DIRECTORY_ID"
105
+ ]
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ The `X-Directory-ID` header is optional. If omitted, the AI will ask which directory to use (or you can call `list_directories` to discover them).
112
+
113
+ ---
114
+
65
115
  ## Configuration
66
116
 
67
117
  | Environment Variable | Required | Description |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "directify-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "MCP server for Directify - manage your directory websites through AI assistants like Claude.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/tools.js CHANGED
@@ -593,6 +593,139 @@ export const toggleArticle = {
593
593
  },
594
594
  };
595
595
 
596
+ // ─── Custom Pages ───
597
+
598
+ export const listPages = {
599
+ name: 'list_pages',
600
+ description: 'List all custom pages in a directory. Custom pages are for static content like About, Terms, comparison pages, etc.',
601
+ inputSchema: {
602
+ type: 'object',
603
+ properties: {
604
+ directory_id: { type: 'string', description: 'Directory ID' },
605
+ },
606
+ },
607
+ handler: async ({ directory_id }) => {
608
+ const dir = resolveDirectory(directory_id);
609
+ const data = await api.get(`/directories/${dir}/pages`);
610
+ return data;
611
+ },
612
+ };
613
+
614
+ export const getPage = {
615
+ name: 'get_page',
616
+ description: 'Get full details of a specific custom page.',
617
+ inputSchema: {
618
+ type: 'object',
619
+ properties: {
620
+ directory_id: { type: 'string', description: 'Directory ID' },
621
+ page_id: { type: 'string', description: 'Page ID' },
622
+ },
623
+ required: ['page_id'],
624
+ },
625
+ handler: async ({ directory_id, page_id }) => {
626
+ const dir = resolveDirectory(directory_id);
627
+ const data = await api.get(`/directories/${dir}/pages/${page_id}`);
628
+ return data.data || data;
629
+ },
630
+ };
631
+
632
+ export const createPage = {
633
+ name: 'create_page',
634
+ description: 'Create a custom page. Great for programmatic SEO pages (comparisons, location pages), About, Terms, etc. Content is markdown.',
635
+ inputSchema: {
636
+ type: 'object',
637
+ properties: {
638
+ directory_id: { type: 'string', description: 'Directory ID' },
639
+ title: { type: 'string', description: 'Page title (required)' },
640
+ slug: { type: 'string', description: 'URL slug (auto-generated from title if not set)' },
641
+ markdown: { type: 'string', description: 'Page content in markdown' },
642
+ placement: { type: 'string', enum: ['navbar', 'footer', 'sidebar', 'unlisted'], description: 'Where the page link appears (default: unlisted)' },
643
+ is_published: { type: 'boolean', description: 'Published status (default: true)' },
644
+ order: { type: 'number', description: 'Sort order for navigation' },
645
+ seo_title: { type: 'string', description: 'SEO title' },
646
+ seo_description: { type: 'string', description: 'SEO meta description' },
647
+ },
648
+ required: ['title'],
649
+ },
650
+ handler: async ({ directory_id, seo_title, seo_description, ...body }) => {
651
+ const dir = resolveDirectory(directory_id);
652
+ if (seo_title || seo_description) {
653
+ body.seo = {};
654
+ if (seo_title) body.seo.title = seo_title;
655
+ if (seo_description) body.seo.description = seo_description;
656
+ }
657
+ const data = await api.post(`/directories/${dir}/pages`, body);
658
+ return data.data || data;
659
+ },
660
+ };
661
+
662
+ export const updatePage = {
663
+ name: 'update_page',
664
+ description: 'Update an existing custom page. Only pass fields you want to change.',
665
+ inputSchema: {
666
+ type: 'object',
667
+ properties: {
668
+ directory_id: { type: 'string', description: 'Directory ID' },
669
+ page_id: { type: 'string', description: 'Page ID to update' },
670
+ title: { type: 'string', description: 'Page title' },
671
+ slug: { type: 'string', description: 'URL slug' },
672
+ markdown: { type: 'string', description: 'Page content in markdown' },
673
+ placement: { type: 'string', enum: ['navbar', 'footer', 'sidebar', 'unlisted'], description: 'Where the page link appears' },
674
+ is_published: { type: 'boolean', description: 'Published status' },
675
+ order: { type: 'number', description: 'Sort order for navigation' },
676
+ seo_title: { type: 'string', description: 'SEO title' },
677
+ seo_description: { type: 'string', description: 'SEO meta description' },
678
+ },
679
+ required: ['page_id'],
680
+ },
681
+ handler: async ({ directory_id, page_id, seo_title, seo_description, ...body }) => {
682
+ const dir = resolveDirectory(directory_id);
683
+ if (seo_title || seo_description) {
684
+ body.seo = {};
685
+ if (seo_title) body.seo.title = seo_title;
686
+ if (seo_description) body.seo.description = seo_description;
687
+ }
688
+ const data = await api.put(`/directories/${dir}/pages/${page_id}`, body);
689
+ return data.data || data;
690
+ },
691
+ };
692
+
693
+ export const deletePage = {
694
+ name: 'delete_page',
695
+ description: 'Delete a custom page from a directory.',
696
+ inputSchema: {
697
+ type: 'object',
698
+ properties: {
699
+ directory_id: { type: 'string', description: 'Directory ID' },
700
+ page_id: { type: 'string', description: 'Page ID to delete' },
701
+ },
702
+ required: ['page_id'],
703
+ },
704
+ handler: async ({ directory_id, page_id }) => {
705
+ const dir = resolveDirectory(directory_id);
706
+ await api.delete(`/directories/${dir}/pages/${page_id}`);
707
+ return { success: true, message: `Page ${page_id} deleted.` };
708
+ },
709
+ };
710
+
711
+ export const togglePage = {
712
+ name: 'toggle_page',
713
+ description: 'Toggle a custom page between published and unpublished status.',
714
+ inputSchema: {
715
+ type: 'object',
716
+ properties: {
717
+ directory_id: { type: 'string', description: 'Directory ID' },
718
+ page_id: { type: 'string', description: 'Page ID to toggle' },
719
+ },
720
+ required: ['page_id'],
721
+ },
722
+ handler: async ({ directory_id, page_id }) => {
723
+ const dir = resolveDirectory(directory_id);
724
+ const data = await api.patch(`/directories/${dir}/pages/${page_id}/toggle`);
725
+ return data.data || data;
726
+ },
727
+ };
728
+
596
729
  // ─── Export all tools ───
597
730
 
598
731
  export const allTools = [
@@ -621,4 +754,10 @@ export const allTools = [
621
754
  updateArticle,
622
755
  deleteArticle,
623
756
  toggleArticle,
757
+ listPages,
758
+ getPage,
759
+ createPage,
760
+ updatePage,
761
+ deletePage,
762
+ togglePage,
624
763
  ];