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.
- package/README.md +50 -0
- package/package.json +1 -1
- 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
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
|
];
|