fs-mcp 1.1.0__tar.gz
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.
- fs_mcp-1.1.0/.github/workflows/release.yaml +48 -0
- fs_mcp-1.1.0/.gitignore +12 -0
- fs_mcp-1.1.0/CHANGELOG.md +59 -0
- fs_mcp-1.1.0/PKG-INFO +150 -0
- fs_mcp-1.1.0/README.md +136 -0
- fs_mcp-1.1.0/pyproject.toml +35 -0
- fs_mcp-1.1.0/src/fs_mcp/__init__.py +0 -0
- fs_mcp-1.1.0/src/fs_mcp/__main__.py +102 -0
- fs_mcp-1.1.0/src/fs_mcp/http_runner.py +28 -0
- fs_mcp-1.1.0/src/fs_mcp/server.py +447 -0
- fs_mcp-1.1.0/src/fs_mcp/web_ui.py +327 -0
- fs_mcp-1.1.0/tests/test_server.py +55 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: Publish to pypi
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- main
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-publish:
|
|
10
|
+
name: Build and publish
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write
|
|
14
|
+
id-token: write
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout repository
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: '3.11'
|
|
25
|
+
|
|
26
|
+
- name: Install Release Tools
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade pip
|
|
29
|
+
pip install python-semantic-release build
|
|
30
|
+
|
|
31
|
+
- name: Release and Build
|
|
32
|
+
id: release_step
|
|
33
|
+
env:
|
|
34
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
35
|
+
run: |
|
|
36
|
+
# 1. Update version, commit, and tag
|
|
37
|
+
semantic-release version
|
|
38
|
+
|
|
39
|
+
# 2. Build the package (using the updated version)
|
|
40
|
+
python -m build
|
|
41
|
+
|
|
42
|
+
# 3. Create the GitHub Release
|
|
43
|
+
semantic-release publish
|
|
44
|
+
|
|
45
|
+
- name: Publish to PyPI
|
|
46
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
47
|
+
with:
|
|
48
|
+
password: ${{ secrets.PYPI_TOKEN }}
|
fs_mcp-1.1.0/.gitignore
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
## v0.4.0 (2026-01-04)
|
|
4
|
+
|
|
5
|
+
### Feature
|
|
6
|
+
|
|
7
|
+
* feat: sync semantic release configuration ([`5258017`](https://github.com/luutuankiet/fs-mcp/commit/525801749d2c1890431d055819b750ddb4f35f42))
|
|
8
|
+
|
|
9
|
+
### Fix
|
|
10
|
+
|
|
11
|
+
* fix: sync semantic version in pyproject.toml ([`c8179e4`](https://github.com/luutuankiet/fs-mcp/commit/c8179e4ea595387eb67162227c7169a4760710fd))
|
|
12
|
+
|
|
13
|
+
## v0.3.0 (2026-01-04)
|
|
14
|
+
|
|
15
|
+
### Chore
|
|
16
|
+
|
|
17
|
+
* chore: cleanup old artifacts & update readme ([`1c2e192`](https://github.com/luutuankiet/fs-mcp/commit/1c2e1923051c81a2247d66af595d33f87ec1f360))
|
|
18
|
+
|
|
19
|
+
### Feature
|
|
20
|
+
|
|
21
|
+
* feat: uses google-genai to convert gemini-compatible json schema ([`e12a99b`](https://github.com/luutuankiet/fs-mcp/commit/e12a99b303be80ae17ff0e8b945c55513ec31b54))
|
|
22
|
+
|
|
23
|
+
## v0.2.0 (2026-01-04)
|
|
24
|
+
|
|
25
|
+
### Chore
|
|
26
|
+
|
|
27
|
+
* chore: webui tool sorted by order ([`69ab178`](https://github.com/luutuankiet/fs-mcp/commit/69ab17880f0f7165b3f04765764904c53de1df11))
|
|
28
|
+
|
|
29
|
+
* chore: bump version ([`c7729c7`](https://github.com/luutuankiet/fs-mcp/commit/c7729c7418eb991a7bdab5875750444190059538))
|
|
30
|
+
|
|
31
|
+
### Feature
|
|
32
|
+
|
|
33
|
+
* feat: roo-style edit file tool and fallback append tool ([`38f5059`](https://github.com/luutuankiet/fs-mcp/commit/38f5059526c1b8943f2b5500c47a2e9c9ab31be2))
|
|
34
|
+
|
|
35
|
+
* feat: auto copy tool response to clipboard ([`d1eba6b`](https://github.com/luutuankiet/fs-mcp/commit/d1eba6b5566fa5e8a8dfc03cc368a18fab9157f9))
|
|
36
|
+
|
|
37
|
+
* feat: dual mode in --ui to spin up both streamlit client and http mcp server ([`51a9074`](https://github.com/luutuankiet/fs-mcp/commit/51a9074639825a342958c312250f8ef6e1b3a904))
|
|
38
|
+
|
|
39
|
+
* feat: web ui uses native fastmcp for emitting jsonschema ([`de79be6`](https://github.com/luutuankiet/fs-mcp/commit/de79be6a1cb616e9bc3e06d0ab3fabbd965f2568))
|
|
40
|
+
|
|
41
|
+
### Fix
|
|
42
|
+
|
|
43
|
+
* fix: precise line for edit_file ([`e889945`](https://github.com/luutuankiet/fs-mcp/commit/e8899452b05367b166256463067300be6bada262))
|
|
44
|
+
|
|
45
|
+
## v0.1.0 (2026-01-04)
|
|
46
|
+
|
|
47
|
+
### Chore
|
|
48
|
+
|
|
49
|
+
* chore: add pypi release GHA ([`31a7bf3`](https://github.com/luutuankiet/fs-mcp/commit/31a7bf3cc6f9b694fecd34e69283543c912870ca))
|
|
50
|
+
|
|
51
|
+
### Feature
|
|
52
|
+
|
|
53
|
+
* feat: add multi file read ([`1098427`](https://github.com/luutuankiet/fs-mcp/commit/1098427164317b44363d7aa64420c9d0afc49678))
|
|
54
|
+
|
|
55
|
+
### Unknown
|
|
56
|
+
|
|
57
|
+
* docs : add readme ([`c3a7218`](https://github.com/luutuankiet/fs-mcp/commit/c3a72188c895cbc4b45d0d350ba22ef0b4143d3d))
|
|
58
|
+
|
|
59
|
+
* init ([`e9d86de`](https://github.com/luutuankiet/fs-mcp/commit/e9d86de61a6f2e7ae1b12afc27c6e1c876705cbe))
|
fs_mcp-1.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fs-mcp
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: A secure MCP filesystem server with Stdio and Web UI modes.
|
|
5
|
+
Author-email: luutuankiet <luutuankiet.ftu2@gmail.com>
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: fastmcp>=0.1.0
|
|
8
|
+
Requires-Dist: google-genai>=1.56.0
|
|
9
|
+
Requires-Dist: httpx>=0.28.1
|
|
10
|
+
Requires-Dist: pydantic>=2.0
|
|
11
|
+
Requires-Dist: streamlit-js-eval>=0.1.5
|
|
12
|
+
Requires-Dist: streamlit>=1.30.0
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# fs-mcp 📂
|
|
16
|
+
|
|
17
|
+
**The "Human-in-the-Loop" Filesystem MCP Server**
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
https://github.com/user-attachments/assets/132acdd9-014c-4ba0-845a-7db74644e655
|
|
22
|
+
|
|
23
|
+
## 💡 Why This Exists
|
|
24
|
+
|
|
25
|
+
I built this because I was tired of jumping through hoops.
|
|
26
|
+
|
|
27
|
+
The promise of the Model Context Protocol (MCP) is incredible, but the reality of using the standard filesystem server hit a few walls for my workflow:
|
|
28
|
+
|
|
29
|
+
1. **The Container Gap:** I do most of my work in Docker. Connecting a local agent (like Claude Desktop) to a filesystem inside a container via Stdio is a networking nightmare.
|
|
30
|
+
2. **The Free Tier Lockout:** I wanted to use the free tier of [Google AI Studio](https://aistudio.google.com/) to edit code, but you can't easily plug MCP into a web interface.
|
|
31
|
+
3. **Schema Hell:** Even if you *do* copy-paste schemas into Gemini, they often break because Gemini's strict validation is only a [subset of the standard OpenAPI spec](https://ai.google.dev/gemini-api/docs/function-calling).
|
|
32
|
+
|
|
33
|
+
**fs-mcp solves this.** It is a Python-based server built on `fastmcp` that treats "Human-in-the-Loop" as a first-class citizen, enabling seamless and interactive collaboration between LLM agents and a developer's local environment.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 🚀 Key Features
|
|
38
|
+
|
|
39
|
+
### 1. HTTP by Default (Remote Ready)
|
|
40
|
+
|
|
41
|
+
It runs a background HTTP server alongside the CLI. You can finally connect agents to remote environments or containers without SSH tunneling wizardry.
|
|
42
|
+
|
|
43
|
+
### 2. Zero-Config Inspector
|
|
44
|
+
|
|
45
|
+
No `npm install inspector`. I baked a **Streamlit Web UI** directly into the package. Launch it, and you instantly have a visual form to test tools, view results, and generate configs.
|
|
46
|
+
|
|
47
|
+
### 3. Copy-Paste Gemini Schemas 📋
|
|
48
|
+
|
|
49
|
+
The UI automatically sanitizes and translates your tool schemas specifically for **Google GenAI**. It strips forbidden keys (`default`, `title`, etc.) so you can paste function definitions directly into AI Studio and start coding for free.
|
|
50
|
+
|
|
51
|
+
### 4. Human-in-the-Loop Diffing 🤝
|
|
52
|
+
|
|
53
|
+
The **`propose_and_review`** tool bridges the gap between agent proposals and human oversight. It opens a VS Code diff window for you to inspect changes.
|
|
54
|
+
|
|
55
|
+
**How it Works:**
|
|
56
|
+
1. The agent calls `propose_and_review` with a code change.
|
|
57
|
+
2. A VS Code window pops up showing the **Diff**.
|
|
58
|
+
3. **To Approve:** Add `` at the very end of the file and Save.
|
|
59
|
+
4. **To Review:** Just edit the code directly in the diff window and Save. The agent will receive your edits as feedback and try again!
|
|
60
|
+
|
|
61
|
+
### 4. Agent-Safe Editing 🛡️
|
|
62
|
+
|
|
63
|
+
Includes a **Roo-Code style `edit_file` tool** with `expected_replacements` validation. This prevents the agent from accidentally mangling your code if it hallucinates line numbers or context.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## ⚡ Quick Start
|
|
68
|
+
|
|
69
|
+
### Run Instantly
|
|
70
|
+
|
|
71
|
+
By default, this command launches the **Web UI (8123)** and a **Background HTTP Server (8124)**.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Allow access to the current dir
|
|
75
|
+
uvx fs-mcp .
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Selective Launch
|
|
79
|
+
|
|
80
|
+
Want to disable a component? Use the flags:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# UI Only (No background HTTP)
|
|
84
|
+
fs-mcp --no-http .
|
|
85
|
+
|
|
86
|
+
# HTTP Only (Headless / Docker mode)
|
|
87
|
+
fs-mcp --no-ui .
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 🔌 Configuration
|
|
93
|
+
|
|
94
|
+
### Claude Desktop (Stdio Mode)
|
|
95
|
+
|
|
96
|
+
Add this to your `claude_desktop_config.json`:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"mcpServers": {
|
|
101
|
+
"fs-mcp": {
|
|
102
|
+
"command": "uvx",
|
|
103
|
+
"args": [
|
|
104
|
+
"fs-mcp",
|
|
105
|
+
"/absolute/path/to/your/project"
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Docker (HTTP Mode)
|
|
113
|
+
|
|
114
|
+
To run inside a container and expose the filesystem to a local agent:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# In your entrypoint or CMD
|
|
118
|
+
uvx fs-mcp --no-ui --http-host 0.0.0.0 --http-port 8124 /app
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## The Toolbox 🧰
|
|
124
|
+
|
|
125
|
+
| Tool | Description |
|
|
126
|
+
| -------------------------- | -------------------------------------------------------------------------- |
|
|
127
|
+
| `propose_and_review` | **Interactive Review:** Opens VS Code diff. Add `` to finalize. |
|
|
128
|
+
| `commit_review` | Finalizes the changes from an interactive review session. |
|
|
129
|
+
| `read_multiple_files` | Reads content of multiple files to save context window. |
|
|
130
|
+
| `directory_tree` | **Fast:** Returns recursive JSON tree. Skips `.venv`/`.git` automatically. |
|
|
131
|
+
| `search_files` | Recursive pattern discovery using `rglob`. |
|
|
132
|
+
| `grounding_search` | **New:** Natural language query for grounded search results. |
|
|
133
|
+
| `read_text_file` | Standard text reader (supports `head`/`tail` for large files). |
|
|
134
|
+
| `list_directory_with_sizes`| Detailed listing including formatted file sizes. |
|
|
135
|
+
| `list_allowed_directories` | List security-approved paths. |
|
|
136
|
+
| `get_file_info` | Metadata retrieval (size, modified time). |
|
|
137
|
+
| `read_media_file` | Returns base64 encoded images/audio. |
|
|
138
|
+
| `write_file` | Creates or overwrites files (atomic operations). |
|
|
139
|
+
| `create_directory` | Create a new directory. |
|
|
140
|
+
| `move_file` | Move or rename files. |
|
|
141
|
+
| `append_text` | Safe fallback for appending content to EOF. |
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## License & Credits
|
|
146
|
+
|
|
147
|
+
Built with ❤️ for the MCP Community by **luutuankiet**.
|
|
148
|
+
Powered by **FastMCP** and **Streamlit**.
|
|
149
|
+
|
|
150
|
+
**Now go build some agents.** 🚀
|
fs_mcp-1.1.0/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# fs-mcp 📂
|
|
2
|
+
|
|
3
|
+
**The "Human-in-the-Loop" Filesystem MCP Server**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
https://github.com/user-attachments/assets/132acdd9-014c-4ba0-845a-7db74644e655
|
|
8
|
+
|
|
9
|
+
## 💡 Why This Exists
|
|
10
|
+
|
|
11
|
+
I built this because I was tired of jumping through hoops.
|
|
12
|
+
|
|
13
|
+
The promise of the Model Context Protocol (MCP) is incredible, but the reality of using the standard filesystem server hit a few walls for my workflow:
|
|
14
|
+
|
|
15
|
+
1. **The Container Gap:** I do most of my work in Docker. Connecting a local agent (like Claude Desktop) to a filesystem inside a container via Stdio is a networking nightmare.
|
|
16
|
+
2. **The Free Tier Lockout:** I wanted to use the free tier of [Google AI Studio](https://aistudio.google.com/) to edit code, but you can't easily plug MCP into a web interface.
|
|
17
|
+
3. **Schema Hell:** Even if you *do* copy-paste schemas into Gemini, they often break because Gemini's strict validation is only a [subset of the standard OpenAPI spec](https://ai.google.dev/gemini-api/docs/function-calling).
|
|
18
|
+
|
|
19
|
+
**fs-mcp solves this.** It is a Python-based server built on `fastmcp` that treats "Human-in-the-Loop" as a first-class citizen, enabling seamless and interactive collaboration between LLM agents and a developer's local environment.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 🚀 Key Features
|
|
24
|
+
|
|
25
|
+
### 1. HTTP by Default (Remote Ready)
|
|
26
|
+
|
|
27
|
+
It runs a background HTTP server alongside the CLI. You can finally connect agents to remote environments or containers without SSH tunneling wizardry.
|
|
28
|
+
|
|
29
|
+
### 2. Zero-Config Inspector
|
|
30
|
+
|
|
31
|
+
No `npm install inspector`. I baked a **Streamlit Web UI** directly into the package. Launch it, and you instantly have a visual form to test tools, view results, and generate configs.
|
|
32
|
+
|
|
33
|
+
### 3. Copy-Paste Gemini Schemas 📋
|
|
34
|
+
|
|
35
|
+
The UI automatically sanitizes and translates your tool schemas specifically for **Google GenAI**. It strips forbidden keys (`default`, `title`, etc.) so you can paste function definitions directly into AI Studio and start coding for free.
|
|
36
|
+
|
|
37
|
+
### 4. Human-in-the-Loop Diffing 🤝
|
|
38
|
+
|
|
39
|
+
The **`propose_and_review`** tool bridges the gap between agent proposals and human oversight. It opens a VS Code diff window for you to inspect changes.
|
|
40
|
+
|
|
41
|
+
**How it Works:**
|
|
42
|
+
1. The agent calls `propose_and_review` with a code change.
|
|
43
|
+
2. A VS Code window pops up showing the **Diff**.
|
|
44
|
+
3. **To Approve:** Add `` at the very end of the file and Save.
|
|
45
|
+
4. **To Review:** Just edit the code directly in the diff window and Save. The agent will receive your edits as feedback and try again!
|
|
46
|
+
|
|
47
|
+
### 4. Agent-Safe Editing 🛡️
|
|
48
|
+
|
|
49
|
+
Includes a **Roo-Code style `edit_file` tool** with `expected_replacements` validation. This prevents the agent from accidentally mangling your code if it hallucinates line numbers or context.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## ⚡ Quick Start
|
|
54
|
+
|
|
55
|
+
### Run Instantly
|
|
56
|
+
|
|
57
|
+
By default, this command launches the **Web UI (8123)** and a **Background HTTP Server (8124)**.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Allow access to the current dir
|
|
61
|
+
uvx fs-mcp .
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Selective Launch
|
|
65
|
+
|
|
66
|
+
Want to disable a component? Use the flags:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# UI Only (No background HTTP)
|
|
70
|
+
fs-mcp --no-http .
|
|
71
|
+
|
|
72
|
+
# HTTP Only (Headless / Docker mode)
|
|
73
|
+
fs-mcp --no-ui .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 🔌 Configuration
|
|
79
|
+
|
|
80
|
+
### Claude Desktop (Stdio Mode)
|
|
81
|
+
|
|
82
|
+
Add this to your `claude_desktop_config.json`:
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"mcpServers": {
|
|
87
|
+
"fs-mcp": {
|
|
88
|
+
"command": "uvx",
|
|
89
|
+
"args": [
|
|
90
|
+
"fs-mcp",
|
|
91
|
+
"/absolute/path/to/your/project"
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Docker (HTTP Mode)
|
|
99
|
+
|
|
100
|
+
To run inside a container and expose the filesystem to a local agent:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# In your entrypoint or CMD
|
|
104
|
+
uvx fs-mcp --no-ui --http-host 0.0.0.0 --http-port 8124 /app
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## The Toolbox 🧰
|
|
110
|
+
|
|
111
|
+
| Tool | Description |
|
|
112
|
+
| -------------------------- | -------------------------------------------------------------------------- |
|
|
113
|
+
| `propose_and_review` | **Interactive Review:** Opens VS Code diff. Add `` to finalize. |
|
|
114
|
+
| `commit_review` | Finalizes the changes from an interactive review session. |
|
|
115
|
+
| `read_multiple_files` | Reads content of multiple files to save context window. |
|
|
116
|
+
| `directory_tree` | **Fast:** Returns recursive JSON tree. Skips `.venv`/`.git` automatically. |
|
|
117
|
+
| `search_files` | Recursive pattern discovery using `rglob`. |
|
|
118
|
+
| `grounding_search` | **New:** Natural language query for grounded search results. |
|
|
119
|
+
| `read_text_file` | Standard text reader (supports `head`/`tail` for large files). |
|
|
120
|
+
| `list_directory_with_sizes`| Detailed listing including formatted file sizes. |
|
|
121
|
+
| `list_allowed_directories` | List security-approved paths. |
|
|
122
|
+
| `get_file_info` | Metadata retrieval (size, modified time). |
|
|
123
|
+
| `read_media_file` | Returns base64 encoded images/audio. |
|
|
124
|
+
| `write_file` | Creates or overwrites files (atomic operations). |
|
|
125
|
+
| `create_directory` | Create a new directory. |
|
|
126
|
+
| `move_file` | Move or rename files. |
|
|
127
|
+
| `append_text` | Safe fallback for appending content to EOF. |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## License & Credits
|
|
132
|
+
|
|
133
|
+
Built with ❤️ for the MCP Community by **luutuankiet**.
|
|
134
|
+
Powered by **FastMCP** and **Streamlit**.
|
|
135
|
+
|
|
136
|
+
**Now go build some agents.** 🚀
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "fs-mcp"
|
|
3
|
+
version = "1.1.0"
|
|
4
|
+
description = "A secure MCP filesystem server with Stdio and Web UI modes."
|
|
5
|
+
authors = [{name = "luutuankiet", email = "luutuankiet.ftu2@gmail.com"}]
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"fastmcp>=0.1.0",
|
|
10
|
+
"google-genai>=1.56.0",
|
|
11
|
+
"httpx>=0.28.1",
|
|
12
|
+
"pydantic>=2.0",
|
|
13
|
+
"streamlit>=1.30.0",
|
|
14
|
+
"streamlit-js-eval>=0.1.5",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
fs-mcp = "fs_mcp.__main__:main"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["hatchling"]
|
|
22
|
+
build-backend = "hatchling.build"
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.wheel]
|
|
25
|
+
packages = ["src/fs_mcp"]
|
|
26
|
+
|
|
27
|
+
[dependency-groups]
|
|
28
|
+
dev = [
|
|
29
|
+
"pytest>=8.0.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
[tool.semantic_release]
|
|
34
|
+
version_toml = ["pyproject.toml:project.version"]
|
|
35
|
+
build_command = "echo 'Build handled by GitHub Action'"
|
|
File without changes
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# src/fs_mcp/__main__.py
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
import subprocess
|
|
6
|
+
import time
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
parser = argparse.ArgumentParser(
|
|
11
|
+
description="fs-mcp server. By default, runs both UI and HTTP servers.",
|
|
12
|
+
formatter_class=argparse.RawTextHelpFormatter
|
|
13
|
+
)
|
|
14
|
+
# UI flags - now inverted to disable the default
|
|
15
|
+
ui_group = parser.add_argument_group('UI Options')
|
|
16
|
+
ui_group.add_argument("--no-ui", action="store_false", dest="run_ui", help="Do not launch the Streamlit Web UI.")
|
|
17
|
+
ui_group.add_argument("--host", default="0.0.0.0", help="Host for the Streamlit UI.")
|
|
18
|
+
ui_group.add_argument("--port", default="8123", type=int, help="Port for the Streamlit UI.")
|
|
19
|
+
|
|
20
|
+
# Background HTTP server flags - now inverted
|
|
21
|
+
http_group = parser.add_argument_group('HTTP Server Options')
|
|
22
|
+
http_group.add_argument("--no-http", action="store_false", dest="run_http", help="Do not run a background HTTP MCP server.")
|
|
23
|
+
http_group.add_argument("--http-host", default="0.0.0.0", help="Host for the background HTTP server.")
|
|
24
|
+
http_group.add_argument("--http-port", type=int, default=8124, help="Port for the background HTTP server.")
|
|
25
|
+
|
|
26
|
+
# Common args
|
|
27
|
+
parser.add_argument("dirs", nargs="*", help="Allowed directories (applies to all server modes).")
|
|
28
|
+
|
|
29
|
+
args, unknown = parser.parse_known_args()
|
|
30
|
+
dirs = args.dirs or [str(Path.cwd())]
|
|
31
|
+
|
|
32
|
+
http_process = None
|
|
33
|
+
try:
|
|
34
|
+
# --- Start Background HTTP Server if requested ---
|
|
35
|
+
if args.run_http:
|
|
36
|
+
# Command to run our dedicated HTTP runner script
|
|
37
|
+
http_cmd = [
|
|
38
|
+
sys.executable, "-m", "fs_mcp.http_runner",
|
|
39
|
+
"--host", args.http_host,
|
|
40
|
+
"--port", str(args.http_port),
|
|
41
|
+
*dirs
|
|
42
|
+
]
|
|
43
|
+
print(f"🚀 Launching background HTTP MCP server process on http://{args.http_host}:{args.http_port}", file=sys.stderr)
|
|
44
|
+
|
|
45
|
+
# Use Popen to start the process without blocking.
|
|
46
|
+
# We pipe stdout/stderr so they don't clutter the main console unless there's an error.
|
|
47
|
+
http_process = subprocess.Popen(http_cmd, stdout=sys.stderr, stderr=sys.stderr)
|
|
48
|
+
|
|
49
|
+
# Give it a moment to start up and check for instant failure.
|
|
50
|
+
time.sleep(2)
|
|
51
|
+
if http_process.poll() is not None:
|
|
52
|
+
print("❌ Background HTTP server failed to start. Check logs.", file=sys.stderr)
|
|
53
|
+
sys.exit(1)
|
|
54
|
+
|
|
55
|
+
# --- Start Foreground Application (UI or wait) ---
|
|
56
|
+
if args.run_ui:
|
|
57
|
+
current_dir = Path(__file__).parent
|
|
58
|
+
ui_path = (current_dir / "web_ui.py").resolve()
|
|
59
|
+
if not ui_path.exists():
|
|
60
|
+
raise FileNotFoundError(f"Could not find web_ui.py at {ui_path}")
|
|
61
|
+
|
|
62
|
+
ui_cmd = [
|
|
63
|
+
sys.executable, "-m", "streamlit", "run", str(ui_path),
|
|
64
|
+
"--server.address", args.host,
|
|
65
|
+
"--server.port", str(args.port),
|
|
66
|
+
"--", *[str(Path(d).resolve()) for d in dirs]
|
|
67
|
+
]
|
|
68
|
+
print(f"🚀 Launching UI on http://{args.host}:{args.port}", file=sys.stderr)
|
|
69
|
+
# This is a blocking call. The script waits here until Streamlit exits.
|
|
70
|
+
subprocess.run(ui_cmd)
|
|
71
|
+
|
|
72
|
+
elif args.run_http:
|
|
73
|
+
# If ONLY the http server is running, we just need to wait.
|
|
74
|
+
print("Background HTTP server is running. Press Ctrl+C to stop.", file=sys.stderr)
|
|
75
|
+
http_process.wait()
|
|
76
|
+
|
|
77
|
+
if not args.run_ui and not args.run_http:
|
|
78
|
+
# Default: run the original stdio server. This should be a direct import.
|
|
79
|
+
from fs_mcp import server
|
|
80
|
+
print("🚀 Launching Stdio MCP server", file=sys.stderr)
|
|
81
|
+
server.initialize(dirs)
|
|
82
|
+
server.mcp.run()
|
|
83
|
+
|
|
84
|
+
except KeyboardInterrupt:
|
|
85
|
+
print("\nCaught interrupt, shutting down...", file=sys.stderr)
|
|
86
|
+
|
|
87
|
+
finally:
|
|
88
|
+
# This block is GUARANTEED to run, ensuring the background process is cleaned up.
|
|
89
|
+
if http_process:
|
|
90
|
+
print("Terminating background HTTP server...", file=sys.stderr)
|
|
91
|
+
http_process.terminate() # Sends SIGTERM for a graceful shutdown
|
|
92
|
+
try:
|
|
93
|
+
# Wait up to 5 seconds for it to shut down
|
|
94
|
+
http_process.wait(timeout=5)
|
|
95
|
+
print("Background server stopped gracefully.", file=sys.stderr)
|
|
96
|
+
except subprocess.TimeoutExpired:
|
|
97
|
+
# If it's stuck, force kill it.
|
|
98
|
+
print("Server did not terminate gracefully, killing.", file=sys.stderr)
|
|
99
|
+
http_process.kill()
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
main()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from fs_mcp import server
|
|
3
|
+
|
|
4
|
+
def main():
|
|
5
|
+
"""
|
|
6
|
+
This is a dedicated entry point for running the FastMCP server in HTTP mode.
|
|
7
|
+
It's designed to be called as a subprocess from the main CLI.
|
|
8
|
+
"""
|
|
9
|
+
parser = argparse.ArgumentParser()
|
|
10
|
+
parser.add_argument("--host", required=True)
|
|
11
|
+
parser.add_argument("--port", type=int, required=True)
|
|
12
|
+
parser.add_argument("dirs", nargs="*")
|
|
13
|
+
args = parser.parse_args()
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
server.initialize(args.dirs)
|
|
17
|
+
server.mcp.run(
|
|
18
|
+
transport="streamable-http",
|
|
19
|
+
host=args.host,
|
|
20
|
+
port=args.port
|
|
21
|
+
)
|
|
22
|
+
except KeyboardInterrupt:
|
|
23
|
+
pass # The main process will handle termination.
|
|
24
|
+
except Exception as e:
|
|
25
|
+
print(f"HTTP runner failed: {e}")
|
|
26
|
+
|
|
27
|
+
if __name__ == "__main__":
|
|
28
|
+
main()
|