mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-04-25 15:25:15 +02:00
Merge branch 'main' into update-pr-template-deprecate-readme
This commit is contained in:
@@ -175,6 +175,35 @@ The server's directory access control follows this flow:
|
|||||||
- Returns:
|
- Returns:
|
||||||
- Directories that this server can read/write from
|
- Directories that this server can read/write from
|
||||||
|
|
||||||
|
### Tool annotations (MCP hints)
|
||||||
|
|
||||||
|
This server sets [MCP ToolAnnotations](https://modelcontextprotocol.io/specification/2025-03-26/server/tools#toolannotations)
|
||||||
|
on each tool so clients can:
|
||||||
|
|
||||||
|
- Distinguish **read‑only** tools from write‑capable tools.
|
||||||
|
- Understand which write operations are **idempotent** (safe to retry with the same arguments).
|
||||||
|
- Highlight operations that may be **destructive** (overwriting or heavily mutating data).
|
||||||
|
|
||||||
|
The mapping for filesystem tools is:
|
||||||
|
|
||||||
|
| Tool | readOnlyHint | idempotentHint | destructiveHint | Notes |
|
||||||
|
|-----------------------------|--------------|----------------|-----------------|--------------------------------------------------|
|
||||||
|
| `read_text_file` | `true` | – | – | Pure read |
|
||||||
|
| `read_media_file` | `true` | – | – | Pure read |
|
||||||
|
| `read_multiple_files` | `true` | – | – | Pure read |
|
||||||
|
| `list_directory` | `true` | – | – | Pure read |
|
||||||
|
| `list_directory_with_sizes` | `true` | – | – | Pure read |
|
||||||
|
| `directory_tree` | `true` | – | – | Pure read |
|
||||||
|
| `search_files` | `true` | – | – | Pure read |
|
||||||
|
| `get_file_info` | `true` | – | – | Pure read |
|
||||||
|
| `list_allowed_directories` | `true` | – | – | Pure read |
|
||||||
|
| `create_directory` | `false` | `true` | `false` | Re‑creating the same dir is a no‑op |
|
||||||
|
| `write_file` | `false` | `true` | `true` | Overwrites existing files |
|
||||||
|
| `edit_file` | `false` | `false` | `true` | Re‑applying edits can fail or double‑apply |
|
||||||
|
| `move_file` | `false` | `false` | `false` | Move/rename only; repeat usually errors |
|
||||||
|
|
||||||
|
> Note: `idempotentHint` and `destructiveHint` are meaningful only when `readOnlyHint` is `false`, as defined by the MCP spec.
|
||||||
|
|
||||||
## Usage with Claude Desktop
|
## Usage with Claude Desktop
|
||||||
Add this to your `claude_desktop_config.json`:
|
Add this to your `claude_desktop_config.json`:
|
||||||
|
|
||||||
|
|||||||
@@ -197,7 +197,8 @@ server.registerTool(
|
|||||||
title: "Read File (Deprecated)",
|
title: "Read File (Deprecated)",
|
||||||
description: "Read the complete contents of a file as text. DEPRECATED: Use read_text_file instead.",
|
description: "Read the complete contents of a file as text. DEPRECATED: Use read_text_file instead.",
|
||||||
inputSchema: ReadTextFileArgsSchema.shape,
|
inputSchema: ReadTextFileArgsSchema.shape,
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: true }
|
||||||
},
|
},
|
||||||
readTextFileHandler
|
readTextFileHandler
|
||||||
);
|
);
|
||||||
@@ -219,7 +220,8 @@ server.registerTool(
|
|||||||
tail: z.number().optional().describe("If provided, returns only the last N lines of the file"),
|
tail: z.number().optional().describe("If provided, returns only the last N lines of the file"),
|
||||||
head: z.number().optional().describe("If provided, returns only the first N lines of the file")
|
head: z.number().optional().describe("If provided, returns only the first N lines of the file")
|
||||||
},
|
},
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: true }
|
||||||
},
|
},
|
||||||
readTextFileHandler
|
readTextFileHandler
|
||||||
);
|
);
|
||||||
@@ -240,7 +242,8 @@ server.registerTool(
|
|||||||
data: z.string(),
|
data: z.string(),
|
||||||
mimeType: z.string()
|
mimeType: z.string()
|
||||||
}))
|
}))
|
||||||
}
|
},
|
||||||
|
annotations: { readOnlyHint: true }
|
||||||
},
|
},
|
||||||
async (args: z.infer<typeof ReadMediaFileArgsSchema>) => {
|
async (args: z.infer<typeof ReadMediaFileArgsSchema>) => {
|
||||||
const validPath = await validatePath(args.path);
|
const validPath = await validatePath(args.path);
|
||||||
@@ -290,7 +293,8 @@ server.registerTool(
|
|||||||
.min(1)
|
.min(1)
|
||||||
.describe("Array of file paths to read. Each path must be a string pointing to a valid file within allowed directories.")
|
.describe("Array of file paths to read. Each path must be a string pointing to a valid file within allowed directories.")
|
||||||
},
|
},
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: true }
|
||||||
},
|
},
|
||||||
async (args: z.infer<typeof ReadMultipleFilesArgsSchema>) => {
|
async (args: z.infer<typeof ReadMultipleFilesArgsSchema>) => {
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
@@ -325,7 +329,8 @@ server.registerTool(
|
|||||||
path: z.string(),
|
path: z.string(),
|
||||||
content: z.string()
|
content: z.string()
|
||||||
},
|
},
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: true }
|
||||||
},
|
},
|
||||||
async (args: z.infer<typeof WriteFileArgsSchema>) => {
|
async (args: z.infer<typeof WriteFileArgsSchema>) => {
|
||||||
const validPath = await validatePath(args.path);
|
const validPath = await validatePath(args.path);
|
||||||
@@ -354,7 +359,8 @@ server.registerTool(
|
|||||||
})),
|
})),
|
||||||
dryRun: z.boolean().default(false).describe("Preview changes using git-style diff format")
|
dryRun: z.boolean().default(false).describe("Preview changes using git-style diff format")
|
||||||
},
|
},
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: false, idempotentHint: false, destructiveHint: true }
|
||||||
},
|
},
|
||||||
async (args: z.infer<typeof EditFileArgsSchema>) => {
|
async (args: z.infer<typeof EditFileArgsSchema>) => {
|
||||||
const validPath = await validatePath(args.path);
|
const validPath = await validatePath(args.path);
|
||||||
@@ -378,7 +384,8 @@ server.registerTool(
|
|||||||
inputSchema: {
|
inputSchema: {
|
||||||
path: z.string()
|
path: z.string()
|
||||||
},
|
},
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: false }
|
||||||
},
|
},
|
||||||
async (args: z.infer<typeof CreateDirectoryArgsSchema>) => {
|
async (args: z.infer<typeof CreateDirectoryArgsSchema>) => {
|
||||||
const validPath = await validatePath(args.path);
|
const validPath = await validatePath(args.path);
|
||||||
@@ -403,7 +410,8 @@ server.registerTool(
|
|||||||
inputSchema: {
|
inputSchema: {
|
||||||
path: z.string()
|
path: z.string()
|
||||||
},
|
},
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: true }
|
||||||
},
|
},
|
||||||
async (args: z.infer<typeof ListDirectoryArgsSchema>) => {
|
async (args: z.infer<typeof ListDirectoryArgsSchema>) => {
|
||||||
const validPath = await validatePath(args.path);
|
const validPath = await validatePath(args.path);
|
||||||
@@ -431,7 +439,8 @@ server.registerTool(
|
|||||||
path: z.string(),
|
path: z.string(),
|
||||||
sortBy: z.enum(["name", "size"]).optional().default("name").describe("Sort entries by name or size")
|
sortBy: z.enum(["name", "size"]).optional().default("name").describe("Sort entries by name or size")
|
||||||
},
|
},
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: true }
|
||||||
},
|
},
|
||||||
async (args: z.infer<typeof ListDirectoryWithSizesArgsSchema>) => {
|
async (args: z.infer<typeof ListDirectoryWithSizesArgsSchema>) => {
|
||||||
const validPath = await validatePath(args.path);
|
const validPath = await validatePath(args.path);
|
||||||
@@ -509,7 +518,8 @@ server.registerTool(
|
|||||||
path: z.string(),
|
path: z.string(),
|
||||||
excludePatterns: z.array(z.string()).optional().default([])
|
excludePatterns: z.array(z.string()).optional().default([])
|
||||||
},
|
},
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: true }
|
||||||
},
|
},
|
||||||
async (args: z.infer<typeof DirectoryTreeArgsSchema>) => {
|
async (args: z.infer<typeof DirectoryTreeArgsSchema>) => {
|
||||||
interface TreeEntry {
|
interface TreeEntry {
|
||||||
@@ -578,7 +588,8 @@ server.registerTool(
|
|||||||
source: z.string(),
|
source: z.string(),
|
||||||
destination: z.string()
|
destination: z.string()
|
||||||
},
|
},
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: false, idempotentHint: false, destructiveHint: false }
|
||||||
},
|
},
|
||||||
async (args: z.infer<typeof MoveFileArgsSchema>) => {
|
async (args: z.infer<typeof MoveFileArgsSchema>) => {
|
||||||
const validSourcePath = await validatePath(args.source);
|
const validSourcePath = await validatePath(args.source);
|
||||||
@@ -608,7 +619,8 @@ server.registerTool(
|
|||||||
pattern: z.string(),
|
pattern: z.string(),
|
||||||
excludePatterns: z.array(z.string()).optional().default([])
|
excludePatterns: z.array(z.string()).optional().default([])
|
||||||
},
|
},
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: true }
|
||||||
},
|
},
|
||||||
async (args: z.infer<typeof SearchFilesArgsSchema>) => {
|
async (args: z.infer<typeof SearchFilesArgsSchema>) => {
|
||||||
const validPath = await validatePath(args.path);
|
const validPath = await validatePath(args.path);
|
||||||
@@ -633,7 +645,8 @@ server.registerTool(
|
|||||||
inputSchema: {
|
inputSchema: {
|
||||||
path: z.string()
|
path: z.string()
|
||||||
},
|
},
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: true }
|
||||||
},
|
},
|
||||||
async (args: z.infer<typeof GetFileInfoArgsSchema>) => {
|
async (args: z.infer<typeof GetFileInfoArgsSchema>) => {
|
||||||
const validPath = await validatePath(args.path);
|
const validPath = await validatePath(args.path);
|
||||||
@@ -658,7 +671,8 @@ server.registerTool(
|
|||||||
"Use this to understand which directories and their nested paths are available " +
|
"Use this to understand which directories and their nested paths are available " +
|
||||||
"before trying to access files.",
|
"before trying to access files.",
|
||||||
inputSchema: {},
|
inputSchema: {},
|
||||||
outputSchema: { content: z.string() }
|
outputSchema: { content: z.string() },
|
||||||
|
annotations: { readOnlyHint: true }
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
const text = `Allowed directories:\n${allowedDirectories.join('\n')}`;
|
const text = `Allowed directories:\n${allowedDirectories.join('\n')}`;
|
||||||
|
|||||||
Reference in New Issue
Block a user