Skip to content

Commit 131f77c

Browse files
authored
Merge branch 'main' into tadasant/add-pulsemcp
2 parents 27ea64d + 46b1bfc commit 131f77c

File tree

3 files changed

+78
-18
lines changed

3 files changed

+78
-18
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Each MCP server is implemented with either the [Typescript MCP SDK](https://gith
1010

1111
These servers aim to demonstrate MCP features and the Typescript and Python SDK.
1212

13-
- **[AWS KB Retrieval](src/aws-kb-retrieval)** - Retrieval from AWS Knowledge Base using Bedrock Agent Runtime
13+
- **[AWS KB Retrieval](src/aws-kb-retrieval-server)** - Retrieval from AWS Knowledge Base using Bedrock Agent Runtime
1414
- **[Brave Search](src/brave-search)** - Web and local search using Brave's Search API
1515
- **[EverArt](src/everart)** - AI image generation using various models
1616
- **[Fetch](src/fetch)** - Web content fetching and conversion for efficient LLM usage
@@ -52,6 +52,7 @@ A growing set of community-developed and maintained servers demonstrates various
5252
> **Note:** Community servers are **untested** and should be used at **your own risk**. They are not affiliated with or endorsed by Anthropic.
5353
5454
- **[MCP Installer](https://github.com/anaisbetts/mcp-installer)** - This server is a server that installs other MCP servers for you.
55+
- **[NS Travel Information](https://github.com/r-huijts/ns-mcp-server)** - Access Dutch Railways (NS) real-time train travel information and disruptions through the official NS API.
5556
- **[Spotify](https://github.com/varunneal/spotify-mcp)** - This MCP allows an LLM to play and use Spotify.
5657
- **[Inoyu](https://github.com/sergehuber/inoyu-mcp-unomi-server)** - Interact with an Apache Unomi CDP customer data platform to retrieve and update customer profiles
5758
- **[Snowflake](https://github.com/datawiz168/mcp-snowflake-service)** - This MCP server enables LLMs to interact with Snowflake databases, allowing for secure and controlled data operations.
@@ -75,6 +76,8 @@ A growing set of community-developed and maintained servers demonstrates various
7576
- **[OpenAPI](https://github.com/snaggle-ai/openapi-mcp-server)** - Interact with [OpenAPI](https://www.openapis.org/) APIs.
7677
- **[Pandoc](https://github.com/vivekVells/mcp-pandoc)** - MCP server for seamless document format conversion using Pandoc, supporting Markdown, HTML, and plain text, with other formats like PDF, csv and docx in development.
7778
- **[HuggingFace Spaces](https://github.com/evalstate/mcp-hfspace)** - Server for using HuggingFace Spaces, supporting Open Source Image, Audio, Text Models and more. Claude Desktop mode for easy integration.
79+
- **[ChatSum](https://github.com/mcpso/mcp-server-chatsum)** - Query and Summarize chat messages with LLM. by [mcpso](https://mcp.so)
80+
- **[Rememberizer AI](https://github.com/skydeckai/mcp-server-rememberizer)** - An MCP server designed for interacting with the Rememberizer data source, facilitating enhanced knowledge retrieval.
7881

7982
## 📚 Resources
8083

@@ -87,6 +90,7 @@ Additional resources on MCP.
8790
- **[mcp-get](https://mcp-get.com)** - Command line tool for installing and managing MCP servers by **[Michael Latman](https://github.com/michaellatman)**
8891
- **[mcp-cli](https://github.com/wong2/mcp-cli)** - A CLI inspector for the Model Context Protocol by **[wong2](https://github.com/wong2)**
8992
- **[r/mcp](https://www.reddit.com/r/mcp)** – A Reddit community dedicated to MCP by **[Frank Fiegel](https://github.com/punkpeye)**
93+
- **[mcp-manager](https://github.com/zueai/mcp-manager)** - Simple Web UI to install and manage MCP servers for Claude Desktop by **[Zue](https://github.com/zueai)**
9094
- **[PulseMCP](https://www.pulsemcp.com)** - An aggregated, searchable directory of MCP servers and other resources by **[Tadas Antanavicius](https://github.com/tadasant)**, **[Mike Coughlin](https://github.com/macoughl)**, and **[Ravina Patel](https://github.com/ravinahp)**
9195

9296
## 🚀 Getting Started

src/filesystem/index.ts

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function expandHome(filepath: string): string {
3535
}
3636

3737
// Store allowed directories in normalized form
38-
const allowedDirectories = args.map(dir =>
38+
const allowedDirectories = args.map(dir =>
3939
normalizePath(path.resolve(expandHome(dir)))
4040
);
4141

@@ -59,7 +59,7 @@ async function validatePath(requestedPath: string): Promise<string> {
5959
const absolute = path.isAbsolute(expandedPath)
6060
? path.resolve(expandedPath)
6161
: path.resolve(process.cwd(), expandedPath);
62-
62+
6363
const normalizedRequested = normalizePath(absolute);
6464

6565
// Check if path is within allowed directories
@@ -127,6 +127,10 @@ const ListDirectoryArgsSchema = z.object({
127127
path: z.string(),
128128
});
129129

130+
const DirectoryTreeArgsSchema = z.object({
131+
path: z.string(),
132+
});
133+
130134
const MoveFileArgsSchema = z.object({
131135
source: z.string(),
132136
destination: z.string(),
@@ -237,7 +241,7 @@ function createUnifiedDiff(originalContent: string, newContent: string, filepath
237241
// Ensure consistent line endings for diff
238242
const normalizedOriginal = normalizeLineEndings(originalContent);
239243
const normalizedNew = normalizeLineEndings(newContent);
240-
244+
241245
return createTwoFilesPatch(
242246
filepath,
243247
filepath,
@@ -255,33 +259,33 @@ async function applyFileEdits(
255259
): Promise<string> {
256260
// Read file content and normalize line endings
257261
const content = normalizeLineEndings(await fs.readFile(filePath, 'utf-8'));
258-
262+
259263
// Apply edits sequentially
260264
let modifiedContent = content;
261265
for (const edit of edits) {
262266
const normalizedOld = normalizeLineEndings(edit.oldText);
263267
const normalizedNew = normalizeLineEndings(edit.newText);
264-
268+
265269
// If exact match exists, use it
266270
if (modifiedContent.includes(normalizedOld)) {
267271
modifiedContent = modifiedContent.replace(normalizedOld, normalizedNew);
268272
continue;
269273
}
270-
274+
271275
// Otherwise, try line-by-line matching with flexibility for whitespace
272276
const oldLines = normalizedOld.split('\n');
273277
const contentLines = modifiedContent.split('\n');
274278
let matchFound = false;
275-
279+
276280
for (let i = 0; i <= contentLines.length - oldLines.length; i++) {
277281
const potentialMatch = contentLines.slice(i, i + oldLines.length);
278-
282+
279283
// Compare lines with normalized whitespace
280284
const isMatch = oldLines.every((oldLine, j) => {
281285
const contentLine = potentialMatch[j];
282286
return oldLine.trim() === contentLine.trim();
283287
});
284-
288+
285289
if (isMatch) {
286290
// Preserve original indentation of first line
287291
const originalIndent = contentLines[i].match(/^\s*/)?.[0] || '';
@@ -296,33 +300,33 @@ async function applyFileEdits(
296300
}
297301
return line;
298302
});
299-
303+
300304
contentLines.splice(i, oldLines.length, ...newLines);
301305
modifiedContent = contentLines.join('\n');
302306
matchFound = true;
303307
break;
304308
}
305309
}
306-
310+
307311
if (!matchFound) {
308312
throw new Error(`Could not find exact match for edit:\n${edit.oldText}`);
309313
}
310314
}
311-
315+
312316
// Create unified diff
313317
const diff = createUnifiedDiff(content, modifiedContent, filePath);
314-
318+
315319
// Format diff with appropriate number of backticks
316320
let numBackticks = 3;
317321
while (diff.includes('`'.repeat(numBackticks))) {
318322
numBackticks++;
319323
}
320324
const formattedDiff = `${'`'.repeat(numBackticks)}diff\n${diff}${'`'.repeat(numBackticks)}\n\n`;
321-
325+
322326
if (!dryRun) {
323327
await fs.writeFile(filePath, modifiedContent, 'utf-8');
324328
}
325-
329+
326330
return formattedDiff;
327331
}
328332

@@ -383,6 +387,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
383387
"finding specific files within a directory. Only works within allowed directories.",
384388
inputSchema: zodToJsonSchema(ListDirectoryArgsSchema) as ToolInput,
385389
},
390+
{
391+
name: "directory_tree",
392+
description:
393+
"Get a recursive tree view of files and directories as a JSON structure. " +
394+
"Each entry includes 'name', 'type' (file/directory), and 'children' for directories. " +
395+
"Files have no children array, while directories always have a children array (which may be empty). " +
396+
"The output is formatted with 2-space indentation for readability. Only works within allowed directories.",
397+
inputSchema: zodToJsonSchema(DirectoryTreeArgsSchema) as ToolInput,
398+
},
386399
{
387400
name: "move_file",
388401
description:
@@ -413,7 +426,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
413426
},
414427
{
415428
name: "list_allowed_directories",
416-
description:
429+
description:
417430
"Returns the list of directories that this server is allowed to access. " +
418431
"Use this to understand which directories are available before trying to access files.",
419432
inputSchema: {
@@ -517,6 +530,49 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
517530
};
518531
}
519532

533+
case "directory_tree": {
534+
const parsed = DirectoryTreeArgsSchema.safeParse(args);
535+
if (!parsed.success) {
536+
throw new Error(`Invalid arguments for directory_tree: ${parsed.error}`);
537+
}
538+
539+
interface TreeEntry {
540+
name: string;
541+
type: 'file' | 'directory';
542+
children?: TreeEntry[];
543+
}
544+
545+
async function buildTree(currentPath: string): Promise<TreeEntry[]> {
546+
const validPath = await validatePath(currentPath);
547+
const entries = await fs.readdir(validPath, {withFileTypes: true});
548+
const result: TreeEntry[] = [];
549+
550+
for (const entry of entries) {
551+
const entryData: TreeEntry = {
552+
name: entry.name,
553+
type: entry.isDirectory() ? 'directory' : 'file'
554+
};
555+
556+
if (entry.isDirectory()) {
557+
const subPath = path.join(currentPath, entry.name);
558+
entryData.children = await buildTree(subPath);
559+
}
560+
561+
result.push(entryData);
562+
}
563+
564+
return result;
565+
}
566+
567+
const treeData = await buildTree(parsed.data.path);
568+
return {
569+
content: [{
570+
type: "text",
571+
text: JSON.stringify(treeData, null, 2)
572+
}],
573+
};
574+
}
575+
520576
case "move_file": {
521577
const parsed = MoveFileArgsSchema.safeParse(args);
522578
if (!parsed.success) {

src/github/schemas.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ export const GitHubIssueSchema = z.object({
274274
created_at: z.string(),
275275
updated_at: z.string(),
276276
closed_at: z.string().nullable(),
277-
body: z.string(),
277+
body: z.string().nullable(),
278278
});
279279

280280
// Pull Request related schemas

0 commit comments

Comments
 (0)