source-context-mcp 0.1.dev3__tar.gz → 0.1.dev5__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.
- source_context_mcp-0.1.dev5/PKG-INFO +209 -0
- source_context_mcp-0.1.dev5/README.md +193 -0
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/pyproject.toml +1 -1
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/pyproject.toml.orig +1 -1
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/cli.py +8 -4
- source_context_mcp-0.1.dev5/src/source_context_mcp/core/metadata.py +16 -0
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/core/settings.py +8 -3
- source_context_mcp-0.1.dev5/src/source_context_mcp/prompts/__init__.py +19 -0
- source_context_mcp-0.1.dev5/src/source_context_mcp/prompts/analyze_architecture.py +30 -0
- source_context_mcp-0.1.dev5/src/source_context_mcp/prompts/prepare_for_change.py +49 -0
- source_context_mcp-0.1.dev5/src/source_context_mcp/prompts/summarize_file.py +33 -0
- source_context_mcp-0.1.dev5/src/source_context_mcp/resources/__init__.py +11 -0
- source_context_mcp-0.1.dev5/src/source_context_mcp/server.py +92 -0
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/tools/__init__.py +4 -1
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/tools/base.py +25 -28
- source_context_mcp-0.1.dev5/src/source_context_mcp/tools/debug.py +27 -0
- source_context_mcp-0.1.dev5/src/source_context_mcp/tools/graph.py +76 -0
- source_context_mcp-0.1.dev5/src/source_context_mcp/tools/vector.py +42 -0
- source_context_mcp-0.1.dev3/PKG-INFO +0 -17
- source_context_mcp-0.1.dev3/README.md +0 -1
- source_context_mcp-0.1.dev3/src/source_context_mcp/server.py +0 -48
- source_context_mcp-0.1.dev3/src/source_context_mcp/tools/debug.py +0 -11
- source_context_mcp-0.1.dev3/src/source_context_mcp/tools/graph.py +0 -48
- source_context_mcp-0.1.dev3/src/source_context_mcp/tools/vector.py +0 -37
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/__init__.py +0 -0
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/__main__.py +0 -0
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/core/__init__.py +0 -0
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/core/api_client.py +0 -0
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/core/context.py +0 -0
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/core/dependencies.py +0 -0
- {source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/py.typed +0 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: source-context-mcp
|
|
3
|
+
Version: 0.1.dev5
|
|
4
|
+
Summary: Local Model Context Protocol (MCP) server for Source Context code intelligence.
|
|
5
|
+
Author: Le Ngoc Tu, Dang Huu Hieu, Huynh Phat Tai
|
|
6
|
+
Author-email: Le Ngoc Tu <tuleaibk@gmail.com>, Dang Huu Hieu <danghuuhieu038@gmail.com>, Huynh Phat Tai <huynhphtai@gmail.com>
|
|
7
|
+
Requires-Dist: httpx>=0.28.1
|
|
8
|
+
Requires-Dist: mcp[cli]>=2.0.0
|
|
9
|
+
Requires-Dist: pydantic>=2.13.4
|
|
10
|
+
Requires-Dist: pydantic-settings>=2.14.2
|
|
11
|
+
Requires-Dist: tomli-w>=1.2.0
|
|
12
|
+
Requires-Dist: typer>=0.27.0
|
|
13
|
+
Requires-Python: >=3.12
|
|
14
|
+
Project-URL: Repository, https://github.com/TuLe142857/source-context
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# Table of Contents
|
|
18
|
+
- [Introduction](#intro)
|
|
19
|
+
- [Requirements](#requirements)
|
|
20
|
+
- [Setup API Server](#setup-api-server)
|
|
21
|
+
- [Setup MCP Server](#setup-mcp-server)
|
|
22
|
+
- [Available CLI Commands](#available-cli-commands)
|
|
23
|
+
- [Available MCP Capabilities](#available-mcp-capabilities)
|
|
24
|
+
- [Tools](#tools)
|
|
25
|
+
- [Resources](#resources)
|
|
26
|
+
- [Prompts](#prompts)
|
|
27
|
+
|
|
28
|
+
# Introduction <a name="intro"></a>
|
|
29
|
+
This MCP server runs on your local machine and communicates over the `stdio` (Standard Input/Output) transport - meaning
|
|
30
|
+
it talks to your coding agent through standard input/output streams rather than over the network.
|
|
31
|
+
|
|
32
|
+
The server is published to `PyPI`, so you can run it directly with the `uvx` command, without a
|
|
33
|
+
manual installation step.
|
|
34
|
+
- Package name: `source-context-mcp`
|
|
35
|
+
- For more details: https://pypi.org/project/source-context-mcp/
|
|
36
|
+
|
|
37
|
+
This MCP server does not analyze your source code by itself. It is a thin bridge:
|
|
38
|
+
- It receives tool calls from your coding agent.
|
|
39
|
+
- It forwards those calls, with your credentials, to a REST API server.
|
|
40
|
+
- That API server is the one actually indexing and analyzing your codebase, and it returns the results back through
|
|
41
|
+
the MCP server to your agent.
|
|
42
|
+
|
|
43
|
+
So before using this MCP server, you need that REST API server up and running (locally or publicly hosted).
|
|
44
|
+
|
|
45
|
+
```mermaid
|
|
46
|
+
flowchart
|
|
47
|
+
Agents["Coding Agents (Claude, Antigravity, ...)"]
|
|
48
|
+
MCP["This MCP Server"]
|
|
49
|
+
API["REST API Server (locally or publicly hosted)"]
|
|
50
|
+
|
|
51
|
+
Agents --> MCP
|
|
52
|
+
MCP --> API
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
# Requirements <a name="requirements"></a>
|
|
56
|
+
- uv: a Python package manager, used to run this MCP locally via the `uvx` command (similar in spirit to `npx` for
|
|
57
|
+
Node.js packages).
|
|
58
|
+
- Python version: 3.12+
|
|
59
|
+
- A running REST API server with authentication enabled — see [Setup MCP Server](#setup-mcp-server) below for setup.
|
|
60
|
+
|
|
61
|
+
# Setup API Server <a name="setup-api-server"></a>
|
|
62
|
+
See: https://github.com/TuLe142857/source-context
|
|
63
|
+
|
|
64
|
+
This server manages your team workspace (a workspace can contain many repositories), handles indexing, analyzes the
|
|
65
|
+
codebase, and exposes the REST API that this MCP server calls. You can host it on your local machine or make it public
|
|
66
|
+
on the internet.
|
|
67
|
+
|
|
68
|
+
# Setup MCP Server <a name="setup-mcp-server"></a>
|
|
69
|
+
## Step 1: Get your Personal Access Token
|
|
70
|
+
Log in to your REST API server and generate a PAT (Personal Access Token) from your account settings. This token
|
|
71
|
+
authenticates the MCP server's requests to the API on your behalf - treat it like a password.
|
|
72
|
+
|
|
73
|
+
## Step 2: Configure the MCP Server
|
|
74
|
+
You have two ways to set the configuration:
|
|
75
|
+
|
|
76
|
+
**Option A — CLI command:**
|
|
77
|
+
```shell
|
|
78
|
+
uvx source-context-mcp config \
|
|
79
|
+
--token <your token> \
|
|
80
|
+
--server-url <server_url>
|
|
81
|
+
```
|
|
82
|
+
Security note: this command may save your token in your shell's command history.
|
|
83
|
+
Prefer Option B below if that's a concern for you.
|
|
84
|
+
|
|
85
|
+
**Option B (recommended) — edit the config file directly:**
|
|
86
|
+
Edit `~/.source_context_mcp/config.toml` (Linux/macOS) or `C:\Users\{username}\.source_context_mcp\config.toml` (Windows):
|
|
87
|
+
```toml
|
|
88
|
+
SERVER_URL = "http://localhost:8000/mcp/v1"
|
|
89
|
+
PAT = "your token"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Step 3: Add MCP server to your coding agent
|
|
93
|
+
This package is published to PyPI, so it can be run via `uvx source-context-mcp run`. It uses `typer` to wrap the MCP
|
|
94
|
+
server as a CLI (Command-Line Interface), so besides `run` it also exposes utility commands like `config`, `show-config`,
|
|
95
|
+
and `config-dir` - see [Available CLI Commands](#available-cli-commands) below.
|
|
96
|
+
|
|
97
|
+
### Antigravity (IDE & CLI)
|
|
98
|
+
Global scope - Edit `~/.gemini/config/mcp_config.json` (Linux/macOS) or
|
|
99
|
+
`C:\Users\{username}\.gemini\config\mcp_config.json` (Windows):
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"mcpServers": {
|
|
103
|
+
"source-context-mcp": {
|
|
104
|
+
"command": "uvx",
|
|
105
|
+
"args": ["source-context-mcp", "run"]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Project scope - Edit `.agents/mcp_config.json`:
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"mcpServers": {
|
|
115
|
+
"source-context-mcp": {
|
|
116
|
+
"command": "uvx",
|
|
117
|
+
"args": ["source-context-mcp", "run"]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Claude Code
|
|
124
|
+
Global scope:
|
|
125
|
+
```shell
|
|
126
|
+
claude mcp add --scope user source-context-mcp -- uvx source-context-mcp run
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Project scope - The following command writes to `.mcp.json`, which is shared with your team when committed to git:
|
|
130
|
+
```shell
|
|
131
|
+
claude mcp add --scope project source-context-mcp -- uvx source-context-mcp run
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Local scope - Not shared with your team:
|
|
135
|
+
```shell
|
|
136
|
+
claude mcp add source-context-mcp -- uvx source-context-mcp run
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Other Agents
|
|
140
|
+
If you're using a different coding agent, check its documentation for how to add a local MCP server over the `stdio`
|
|
141
|
+
transport. Most agents accept a generic JSON block like this:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"mcpServers": {
|
|
146
|
+
"source-context-mcp": {
|
|
147
|
+
"command": "uvx",
|
|
148
|
+
"args": ["source-context-mcp", "run"]
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
# Available CLI Commands <a name="available-cli-commands"></a>
|
|
155
|
+
The package installs a `source-context-mcp` CLI (built with `typer`). Run `uvx source-context-mcp --help` at any time
|
|
156
|
+
to see this list from the tool itself.
|
|
157
|
+
|
|
158
|
+
| Command | Description | Options |
|
|
159
|
+
|---------------|------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|
|
|
160
|
+
| `run` | Run the MCP server over the `stdio` transport. | `--server-url <url>`, `--token <token>`, `--workspace-id <id>` - override the saved config for this run only |
|
|
161
|
+
| `config` | Save configuration (server URL, token, default workspace ID) to the config file. | `--server-url <url>`, `--token <token>`, `--workspace-id <id>` |
|
|
162
|
+
| `show-config` | Print the current configuration. Secret values (e.g. the token) are masked as `*`. | |
|
|
163
|
+
| `config-dir` | Print the path to the configuration directory. | |
|
|
164
|
+
|
|
165
|
+
Global option (on the base `source-context-mcp` command):
|
|
166
|
+
- `--version` / `-v` - print the installed package version and exit.
|
|
167
|
+
|
|
168
|
+
# Available MCP Capabilities <a name="available-mcp-capabilities"></a>
|
|
169
|
+
|
|
170
|
+
## Tools <a name="tools"></a>
|
|
171
|
+
Tools are like functions that this MCP server exposes to your coding agent. This server provides the following tools:
|
|
172
|
+
|
|
173
|
+
### General
|
|
174
|
+
- `list_workspaces()` - List all workspaces accessible to the current user.
|
|
175
|
+
- `get_path_settings(path)` - Get the default workspace and repository settings configured for a local path.
|
|
176
|
+
- `default_workspace()` - Get the server-configured default workspace ID, used when the current path has no path-specific default.
|
|
177
|
+
- `list_repositories(workspace_id)` - List all repositories in a workspace.
|
|
178
|
+
- `list_branches(workspace_id, repository_id)` - List all branches in a repository.
|
|
179
|
+
- `list_projects(workspace_id, repository_id, branch_name)` - List all projects on a branch.
|
|
180
|
+
|
|
181
|
+
### Graph (code structure & relationships)
|
|
182
|
+
- `list_files_in_project(project_id)` - List all files in a project.
|
|
183
|
+
- `get_file_structure(file_id)` - Get a file's structure: classes and methods.
|
|
184
|
+
- `get_file_content(file_id)` - Read a file's full content.
|
|
185
|
+
- `get_node_info(node_id)` - Get node metadata: id, name, file_id, position in file.
|
|
186
|
+
- `get_node_content(node_id)` - Get a node's source content.
|
|
187
|
+
- `find_node_usages(node_id)` - Find all nodes that call or reference the specified node.
|
|
188
|
+
- `find_node_callees(node_id)` - Find all nodes that the specified node calls or references.
|
|
189
|
+
- `find_node_by_name(name, node_type)` - *Not implemented yet.*
|
|
190
|
+
|
|
191
|
+
### Vector (semantic search)
|
|
192
|
+
- `search_in_workspace(query, workspace_id, top_k)` - Semantic search across all repositories and branches in a workspace.
|
|
193
|
+
- `search_in_repo_and_branch(repository_id, branch_name, query, top_k)` - Semantic search scoped to a specific repository and branch.
|
|
194
|
+
|
|
195
|
+
> Note: development builds (versions containing `dev`) also register an internal `debug` tool for inspecting the
|
|
196
|
+
> server's configuration. It is not available in stable releases.
|
|
197
|
+
|
|
198
|
+
## Resources <a name="resources"></a>
|
|
199
|
+
Not implemented yet.
|
|
200
|
+
|
|
201
|
+
## Prompts <a name="prompts"></a>
|
|
202
|
+
Prompts are reusable, pre-written instructions your coding agent can pull in as a starting point for a task. This
|
|
203
|
+
server provides the following prompts:
|
|
204
|
+
|
|
205
|
+
- `analyze_architecture(project)` - Analyze the software architecture of the given project: locate it, inspect its
|
|
206
|
+
file/project structure, and trace dependencies to produce a structured architecture summary.
|
|
207
|
+
- `prepare_for_change()` - Intended for use before refactoring, to look up usages of a function, class, or other
|
|
208
|
+
object and assess the blast radius of a change. *(work in progress)*
|
|
209
|
+
- `summarize_file()` - Summarize a file. *(work in progress)*
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Table of Contents
|
|
2
|
+
- [Introduction](#intro)
|
|
3
|
+
- [Requirements](#requirements)
|
|
4
|
+
- [Setup API Server](#setup-api-server)
|
|
5
|
+
- [Setup MCP Server](#setup-mcp-server)
|
|
6
|
+
- [Available CLI Commands](#available-cli-commands)
|
|
7
|
+
- [Available MCP Capabilities](#available-mcp-capabilities)
|
|
8
|
+
- [Tools](#tools)
|
|
9
|
+
- [Resources](#resources)
|
|
10
|
+
- [Prompts](#prompts)
|
|
11
|
+
|
|
12
|
+
# Introduction <a name="intro"></a>
|
|
13
|
+
This MCP server runs on your local machine and communicates over the `stdio` (Standard Input/Output) transport - meaning
|
|
14
|
+
it talks to your coding agent through standard input/output streams rather than over the network.
|
|
15
|
+
|
|
16
|
+
The server is published to `PyPI`, so you can run it directly with the `uvx` command, without a
|
|
17
|
+
manual installation step.
|
|
18
|
+
- Package name: `source-context-mcp`
|
|
19
|
+
- For more details: https://pypi.org/project/source-context-mcp/
|
|
20
|
+
|
|
21
|
+
This MCP server does not analyze your source code by itself. It is a thin bridge:
|
|
22
|
+
- It receives tool calls from your coding agent.
|
|
23
|
+
- It forwards those calls, with your credentials, to a REST API server.
|
|
24
|
+
- That API server is the one actually indexing and analyzing your codebase, and it returns the results back through
|
|
25
|
+
the MCP server to your agent.
|
|
26
|
+
|
|
27
|
+
So before using this MCP server, you need that REST API server up and running (locally or publicly hosted).
|
|
28
|
+
|
|
29
|
+
```mermaid
|
|
30
|
+
flowchart
|
|
31
|
+
Agents["Coding Agents (Claude, Antigravity, ...)"]
|
|
32
|
+
MCP["This MCP Server"]
|
|
33
|
+
API["REST API Server (locally or publicly hosted)"]
|
|
34
|
+
|
|
35
|
+
Agents --> MCP
|
|
36
|
+
MCP --> API
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
# Requirements <a name="requirements"></a>
|
|
40
|
+
- uv: a Python package manager, used to run this MCP locally via the `uvx` command (similar in spirit to `npx` for
|
|
41
|
+
Node.js packages).
|
|
42
|
+
- Python version: 3.12+
|
|
43
|
+
- A running REST API server with authentication enabled — see [Setup MCP Server](#setup-mcp-server) below for setup.
|
|
44
|
+
|
|
45
|
+
# Setup API Server <a name="setup-api-server"></a>
|
|
46
|
+
See: https://github.com/TuLe142857/source-context
|
|
47
|
+
|
|
48
|
+
This server manages your team workspace (a workspace can contain many repositories), handles indexing, analyzes the
|
|
49
|
+
codebase, and exposes the REST API that this MCP server calls. You can host it on your local machine or make it public
|
|
50
|
+
on the internet.
|
|
51
|
+
|
|
52
|
+
# Setup MCP Server <a name="setup-mcp-server"></a>
|
|
53
|
+
## Step 1: Get your Personal Access Token
|
|
54
|
+
Log in to your REST API server and generate a PAT (Personal Access Token) from your account settings. This token
|
|
55
|
+
authenticates the MCP server's requests to the API on your behalf - treat it like a password.
|
|
56
|
+
|
|
57
|
+
## Step 2: Configure the MCP Server
|
|
58
|
+
You have two ways to set the configuration:
|
|
59
|
+
|
|
60
|
+
**Option A — CLI command:**
|
|
61
|
+
```shell
|
|
62
|
+
uvx source-context-mcp config \
|
|
63
|
+
--token <your token> \
|
|
64
|
+
--server-url <server_url>
|
|
65
|
+
```
|
|
66
|
+
Security note: this command may save your token in your shell's command history.
|
|
67
|
+
Prefer Option B below if that's a concern for you.
|
|
68
|
+
|
|
69
|
+
**Option B (recommended) — edit the config file directly:**
|
|
70
|
+
Edit `~/.source_context_mcp/config.toml` (Linux/macOS) or `C:\Users\{username}\.source_context_mcp\config.toml` (Windows):
|
|
71
|
+
```toml
|
|
72
|
+
SERVER_URL = "http://localhost:8000/mcp/v1"
|
|
73
|
+
PAT = "your token"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Step 3: Add MCP server to your coding agent
|
|
77
|
+
This package is published to PyPI, so it can be run via `uvx source-context-mcp run`. It uses `typer` to wrap the MCP
|
|
78
|
+
server as a CLI (Command-Line Interface), so besides `run` it also exposes utility commands like `config`, `show-config`,
|
|
79
|
+
and `config-dir` - see [Available CLI Commands](#available-cli-commands) below.
|
|
80
|
+
|
|
81
|
+
### Antigravity (IDE & CLI)
|
|
82
|
+
Global scope - Edit `~/.gemini/config/mcp_config.json` (Linux/macOS) or
|
|
83
|
+
`C:\Users\{username}\.gemini\config\mcp_config.json` (Windows):
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"mcpServers": {
|
|
87
|
+
"source-context-mcp": {
|
|
88
|
+
"command": "uvx",
|
|
89
|
+
"args": ["source-context-mcp", "run"]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Project scope - Edit `.agents/mcp_config.json`:
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"mcpServers": {
|
|
99
|
+
"source-context-mcp": {
|
|
100
|
+
"command": "uvx",
|
|
101
|
+
"args": ["source-context-mcp", "run"]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Claude Code
|
|
108
|
+
Global scope:
|
|
109
|
+
```shell
|
|
110
|
+
claude mcp add --scope user source-context-mcp -- uvx source-context-mcp run
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Project scope - The following command writes to `.mcp.json`, which is shared with your team when committed to git:
|
|
114
|
+
```shell
|
|
115
|
+
claude mcp add --scope project source-context-mcp -- uvx source-context-mcp run
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Local scope - Not shared with your team:
|
|
119
|
+
```shell
|
|
120
|
+
claude mcp add source-context-mcp -- uvx source-context-mcp run
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Other Agents
|
|
124
|
+
If you're using a different coding agent, check its documentation for how to add a local MCP server over the `stdio`
|
|
125
|
+
transport. Most agents accept a generic JSON block like this:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"mcpServers": {
|
|
130
|
+
"source-context-mcp": {
|
|
131
|
+
"command": "uvx",
|
|
132
|
+
"args": ["source-context-mcp", "run"]
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
# Available CLI Commands <a name="available-cli-commands"></a>
|
|
139
|
+
The package installs a `source-context-mcp` CLI (built with `typer`). Run `uvx source-context-mcp --help` at any time
|
|
140
|
+
to see this list from the tool itself.
|
|
141
|
+
|
|
142
|
+
| Command | Description | Options |
|
|
143
|
+
|---------------|------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|
|
|
144
|
+
| `run` | Run the MCP server over the `stdio` transport. | `--server-url <url>`, `--token <token>`, `--workspace-id <id>` - override the saved config for this run only |
|
|
145
|
+
| `config` | Save configuration (server URL, token, default workspace ID) to the config file. | `--server-url <url>`, `--token <token>`, `--workspace-id <id>` |
|
|
146
|
+
| `show-config` | Print the current configuration. Secret values (e.g. the token) are masked as `*`. | |
|
|
147
|
+
| `config-dir` | Print the path to the configuration directory. | |
|
|
148
|
+
|
|
149
|
+
Global option (on the base `source-context-mcp` command):
|
|
150
|
+
- `--version` / `-v` - print the installed package version and exit.
|
|
151
|
+
|
|
152
|
+
# Available MCP Capabilities <a name="available-mcp-capabilities"></a>
|
|
153
|
+
|
|
154
|
+
## Tools <a name="tools"></a>
|
|
155
|
+
Tools are like functions that this MCP server exposes to your coding agent. This server provides the following tools:
|
|
156
|
+
|
|
157
|
+
### General
|
|
158
|
+
- `list_workspaces()` - List all workspaces accessible to the current user.
|
|
159
|
+
- `get_path_settings(path)` - Get the default workspace and repository settings configured for a local path.
|
|
160
|
+
- `default_workspace()` - Get the server-configured default workspace ID, used when the current path has no path-specific default.
|
|
161
|
+
- `list_repositories(workspace_id)` - List all repositories in a workspace.
|
|
162
|
+
- `list_branches(workspace_id, repository_id)` - List all branches in a repository.
|
|
163
|
+
- `list_projects(workspace_id, repository_id, branch_name)` - List all projects on a branch.
|
|
164
|
+
|
|
165
|
+
### Graph (code structure & relationships)
|
|
166
|
+
- `list_files_in_project(project_id)` - List all files in a project.
|
|
167
|
+
- `get_file_structure(file_id)` - Get a file's structure: classes and methods.
|
|
168
|
+
- `get_file_content(file_id)` - Read a file's full content.
|
|
169
|
+
- `get_node_info(node_id)` - Get node metadata: id, name, file_id, position in file.
|
|
170
|
+
- `get_node_content(node_id)` - Get a node's source content.
|
|
171
|
+
- `find_node_usages(node_id)` - Find all nodes that call or reference the specified node.
|
|
172
|
+
- `find_node_callees(node_id)` - Find all nodes that the specified node calls or references.
|
|
173
|
+
- `find_node_by_name(name, node_type)` - *Not implemented yet.*
|
|
174
|
+
|
|
175
|
+
### Vector (semantic search)
|
|
176
|
+
- `search_in_workspace(query, workspace_id, top_k)` - Semantic search across all repositories and branches in a workspace.
|
|
177
|
+
- `search_in_repo_and_branch(repository_id, branch_name, query, top_k)` - Semantic search scoped to a specific repository and branch.
|
|
178
|
+
|
|
179
|
+
> Note: development builds (versions containing `dev`) also register an internal `debug` tool for inspecting the
|
|
180
|
+
> server's configuration. It is not available in stable releases.
|
|
181
|
+
|
|
182
|
+
## Resources <a name="resources"></a>
|
|
183
|
+
Not implemented yet.
|
|
184
|
+
|
|
185
|
+
## Prompts <a name="prompts"></a>
|
|
186
|
+
Prompts are reusable, pre-written instructions your coding agent can pull in as a starting point for a task. This
|
|
187
|
+
server provides the following prompts:
|
|
188
|
+
|
|
189
|
+
- `analyze_architecture(project)` - Analyze the software architecture of the given project: locate it, inspect its
|
|
190
|
+
file/project structure, and trace dependencies to produce a structured architecture summary.
|
|
191
|
+
- `prepare_for_change()` - Intended for use before refactoring, to look up usages of a function, class, or other
|
|
192
|
+
object and assess the blast radius of a change. *(work in progress)*
|
|
193
|
+
- `summarize_file()` - Summarize a file. *(work in progress)*
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import importlib.metadata
|
|
2
|
-
|
|
3
1
|
import typer
|
|
4
2
|
|
|
5
|
-
from .core.
|
|
3
|
+
from .core.metadata import get_package_version
|
|
4
|
+
from .core.settings import CONFIG_DIR, CONFIG_FILE, get_settings, get_settings_with_overrides, write_settings
|
|
6
5
|
from .server import create_server
|
|
7
6
|
|
|
8
7
|
cli = typer.Typer()
|
|
@@ -51,9 +50,14 @@ def show_config():
|
|
|
51
50
|
typer.echo(settings_json)
|
|
52
51
|
|
|
53
52
|
|
|
53
|
+
@cli.command(name="config-dir", help="Show the MCP server's configuration directory")
|
|
54
|
+
def show_config_dir():
|
|
55
|
+
typer.echo(CONFIG_DIR)
|
|
56
|
+
|
|
57
|
+
|
|
54
58
|
@cli.callback(invoke_without_command=True)
|
|
55
59
|
def version_check(
|
|
56
60
|
version: bool = typer.Option(False, "--version", "-v", help="Show version"),
|
|
57
61
|
):
|
|
58
62
|
if version:
|
|
59
|
-
typer.echo(
|
|
63
|
+
typer.echo(get_package_version())
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import importlib.metadata
|
|
2
|
+
from functools import lru_cache
|
|
3
|
+
|
|
4
|
+
_PACKAGE_NAME = "source-context-mcp"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@lru_cache
|
|
8
|
+
def get_package_version() -> str:
|
|
9
|
+
"""
|
|
10
|
+
Get package version.
|
|
11
|
+
Read from ``pyproject.toml`` file.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
This package version.
|
|
15
|
+
"""
|
|
16
|
+
return importlib.metadata.version("source-context-mcp")
|
{source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/core/settings.py
RENAMED
|
@@ -14,17 +14,20 @@ class Settings(BaseSettings):
|
|
|
14
14
|
toml_file=str(CONFIG_FILE),
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
-
SERVER_URL: str = "
|
|
18
|
-
"""Server URL. Include prefix.
|
|
17
|
+
SERVER_URL: str = "http://localhost:8000/mcp/v1"
|
|
18
|
+
"""Server URL. Include prefix. Default: http://localhost:8000/mcp/v1"""
|
|
19
19
|
|
|
20
20
|
PAT: SecretStr = SecretStr("Default")
|
|
21
21
|
"""Personal access token"""
|
|
22
22
|
|
|
23
|
-
DEFAULT_WORKSPACE_ID: int =
|
|
23
|
+
DEFAULT_WORKSPACE_ID: int | None = None
|
|
24
24
|
"""Default Workspace ID"""
|
|
25
25
|
|
|
26
26
|
PATH_WORKSPACE: dict[str, int] = {}
|
|
27
|
+
"""Map [path, default_workspace_id]]"""
|
|
28
|
+
|
|
27
29
|
PATH_REPO: dict[str, int] = {}
|
|
30
|
+
"""Map [path, default_repo_id]]"""
|
|
28
31
|
|
|
29
32
|
@classmethod
|
|
30
33
|
def settings_customise_sources(
|
|
@@ -85,6 +88,8 @@ def write_settings(settings: Settings):
|
|
|
85
88
|
"SERVER_URL": settings.SERVER_URL,
|
|
86
89
|
"PAT": settings.PAT.get_secret_value(),
|
|
87
90
|
"DEFAULT_WORKSPACE_ID": settings.DEFAULT_WORKSPACE_ID,
|
|
91
|
+
"PATH_WORKSPACE": settings.PATH_WORKSPACE,
|
|
92
|
+
"PATH_REPO": settings.PATH_REPO,
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
with CONFIG_FILE.open("wb") as f:
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from mcp.server import MCPServer
|
|
2
|
+
|
|
3
|
+
from .analyze_architecture import register_prompts as register_analyze_architecture
|
|
4
|
+
from .prepare_for_change import register_prompts as register_prepare_for_change
|
|
5
|
+
from .summarize_file import register_prompts as register_summarize_file
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register_prompts(mcp: MCPServer) -> None:
|
|
9
|
+
"""
|
|
10
|
+
Register prompts for MCP server
|
|
11
|
+
Args:
|
|
12
|
+
mcp: MCPServer instance
|
|
13
|
+
"""
|
|
14
|
+
register_analyze_architecture(mcp)
|
|
15
|
+
register_prepare_for_change(mcp)
|
|
16
|
+
register_summarize_file(mcp)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = ["register_prompts"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from mcp.server import MCPServer
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def register_prompts(mcp: MCPServer):
|
|
8
|
+
@mcp.prompt(
|
|
9
|
+
description="Analyze the software architecture of the given project",
|
|
10
|
+
)
|
|
11
|
+
def analyze_architecture(project: Annotated[str, Field(description="Project name")]):
|
|
12
|
+
return f"""
|
|
13
|
+
Analyze the software architecture of the project: '{project}'.
|
|
14
|
+
|
|
15
|
+
Step 1:Identify the Project:
|
|
16
|
+
- If the exact project location or repository is unclear, use tools such as `list_projects`, `list_repositories`, \
|
|
17
|
+
or `list_branches` to locate and confirm the target project.
|
|
18
|
+
|
|
19
|
+
Step 2: Analyze Project Structure (Token-Efficient Approach):
|
|
20
|
+
- Start by calling `get_file_structure` (or `list_files`) to inspect the directory layout.
|
|
21
|
+
- *Best Practice*: Always prefer `get_file_structure` first, as it returns only the structure without full source \
|
|
22
|
+
code, conserving API tokens.
|
|
23
|
+
- Only call `get_file_content` for specific files when you need detailed implementation insights.
|
|
24
|
+
|
|
25
|
+
Step 3: Deepen Architecture Analysis(If needed):
|
|
26
|
+
- Depending on the architecture aspects you are investigating, use additional tools such as `find_node_usages`, \
|
|
27
|
+
`find_node_callees`, or any other relevant analysis tools to trace dependencies and data flow.
|
|
28
|
+
|
|
29
|
+
Provide a clear, structured summary of the architectural patterns, key components, and relationships found in the \
|
|
30
|
+
project."""
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from mcp.server import MCPServer
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def register_prompts(mcp: MCPServer):
|
|
8
|
+
@mcp.prompt(
|
|
9
|
+
description="Use when refactor code. Look for object(function, class, ...) usage to detect refactor effect"
|
|
10
|
+
)
|
|
11
|
+
def prepare_for_change(
|
|
12
|
+
target: Annotated[
|
|
13
|
+
str, Field(description="Name of the function, class, method, or variable you are about to change.")
|
|
14
|
+
],
|
|
15
|
+
project: Annotated[str, Field(description="Project name where the target is located, if known.")] = "",
|
|
16
|
+
change_description: Annotated[
|
|
17
|
+
str, Field(description="Optional description of the change you intend to make.")
|
|
18
|
+
] = "",
|
|
19
|
+
):
|
|
20
|
+
change_note = f"\nIntended change: {change_description}\n" if change_description else ""
|
|
21
|
+
project_note = f" in project '{project}'" if project else ""
|
|
22
|
+
|
|
23
|
+
return f"""
|
|
24
|
+
Prepare for a change to '{target}'{project_note}.{change_note}
|
|
25
|
+
|
|
26
|
+
Step 1: Locate the Target:
|
|
27
|
+
- If the exact project, file, or node is unclear, use `list_projects`, `list_repositories`, `list_branches`, or \
|
|
28
|
+
semantic search (`search_in_workspace` / `search_in_repo_and_branch`) to find it.
|
|
29
|
+
- Once the project is known, prefer `list_files_in_project` and `get_file_structure` to locate the node without \
|
|
30
|
+
reading full file contents.
|
|
31
|
+
- Use `get_node_info` to confirm you have the right node before proceeding.
|
|
32
|
+
|
|
33
|
+
Step 2: Understand the Current Implementation:
|
|
34
|
+
- Call `get_node_content` on the target node to review its current signature and behavior.
|
|
35
|
+
|
|
36
|
+
Step 3: Assess the Blast Radius:
|
|
37
|
+
- Call `find_node_usages` on the target node to list every node that calls or references it. This is the primary \
|
|
38
|
+
signal for how risky the change is - the more usages, the wider the blast radius.
|
|
39
|
+
- For each usage that could be affected (e.g. call sites depending on a changed signature, return type, or \
|
|
40
|
+
behavior), inspect it with `get_node_content` or `get_file_content` to understand exactly how the target is used.
|
|
41
|
+
|
|
42
|
+
Step 4: Check Internal Dependencies (if the change affects internal logic):
|
|
43
|
+
- Call `find_node_callees` on the target node to see what it depends on, so you don't break assumptions the \
|
|
44
|
+
target relies on.
|
|
45
|
+
|
|
46
|
+
Step 5: Summarize:
|
|
47
|
+
- List every affected file/node found in Step 3.
|
|
48
|
+
- Flag usages that are likely to break or need updating given the intended change.
|
|
49
|
+
- Recommend what should be tested or reviewed before making the change."""
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from mcp.server import MCPServer
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def register_prompts(mcp: MCPServer):
|
|
8
|
+
@mcp.prompt(description="Summarize file")
|
|
9
|
+
def summarize_file(
|
|
10
|
+
file: Annotated[str, Field(description="File path or file name to summarize.")],
|
|
11
|
+
project: Annotated[
|
|
12
|
+
str, Field(description="Project name the file belongs to, to help locate it if the path is ambiguous.")
|
|
13
|
+
] = "",
|
|
14
|
+
):
|
|
15
|
+
project_note = f" in project '{project}'" if project else ""
|
|
16
|
+
|
|
17
|
+
return f"""
|
|
18
|
+
Summarize the file '{file}'{project_note}.
|
|
19
|
+
|
|
20
|
+
Step 1: Locate the File:
|
|
21
|
+
- If the exact project or file ID is unclear, use `list_projects` to find the project, then `list_files_in_project` \
|
|
22
|
+
to find the matching file.
|
|
23
|
+
|
|
24
|
+
Step 2: Inspect Structure (Token-Efficient Approach):
|
|
25
|
+
- Start with `get_file_structure` to see the file's classes, methods, and functions without reading the full \
|
|
26
|
+
source.
|
|
27
|
+
- Only call `get_file_content` if you need the actual implementation details to write an accurate summary.
|
|
28
|
+
|
|
29
|
+
Step 3: Summarize:
|
|
30
|
+
- State the file's purpose and its role within the project.
|
|
31
|
+
- List its main classes/functions and what each one does, at a high level.
|
|
32
|
+
- Note any notable dependencies or relationships to other parts of the codebase (use `find_node_usages` or \
|
|
33
|
+
`find_node_callees` on specific nodes only if that context is needed)."""
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from collections.abc import AsyncIterator
|
|
2
|
+
from contextlib import asynccontextmanager
|
|
3
|
+
|
|
4
|
+
from mcp.server import MCPServer
|
|
5
|
+
|
|
6
|
+
from source_context_mcp.core import ApiClient, AppContext, get_settings_with_overrides
|
|
7
|
+
from source_context_mcp.core.metadata import get_package_version
|
|
8
|
+
from source_context_mcp.prompts import register_prompts
|
|
9
|
+
from source_context_mcp.resources import register_resources
|
|
10
|
+
from source_context_mcp.tools import register_tools
|
|
11
|
+
|
|
12
|
+
SERVER_DESCRIPTION = """
|
|
13
|
+
SourceContextMCP is an MCP server that gives AI coding assistants structured, queryable access to indexed source code \
|
|
14
|
+
across workspaces, repositories, branches, and projects. It exposes tools to browse project/file structure, read file \
|
|
15
|
+
and code-node content, traverse call/reference graphs between code nodes, and run semantic (vector) search over a \
|
|
16
|
+
codebase — letting an agent explore and understand large, multi-repository codebases without needing direct local file \
|
|
17
|
+
access."
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
SERVER_INSTRUCTIONS = """
|
|
21
|
+
SourceContextMCP gives read-only access to a pre-indexed source of truth for one or more codebases, organized as: \
|
|
22
|
+
workspace -> repository -> branch -> project -> file -> node (class/method/function/variable/...).
|
|
23
|
+
|
|
24
|
+
Most tools require one or more IDs (workspace_id, repository_id, project_id, file_id, node_id). These are opaque and \
|
|
25
|
+
server-assigned - never guess them. Always obtain them from the matching listing tool or reuse an ID from a previous \
|
|
26
|
+
call.
|
|
27
|
+
|
|
28
|
+
Resolve scope before calling graph or search tools. Try get_path_settings(path) first if the user is working inside a \
|
|
29
|
+
local checkout; fall back to default_workspace() if no path-specific config exists. Otherwise narrow down with \
|
|
30
|
+
list_workspaces -> list_repositories -> list_branches -> list_projects.
|
|
31
|
+
|
|
32
|
+
Once you have a project_id, use list_files_in_project for file_ids, then get_file_structure before get_file_content \
|
|
33
|
+
when you only need an overview or a node_id - avoid reading full file contents unless necessary.
|
|
34
|
+
|
|
35
|
+
For call-graph relationships on a node_id: use find_node_usages before renaming or changing a signature to assess \
|
|
36
|
+
blast radius, and find_node_callees to understand what a symbol depends on. Prefer graph traversal over semantic \
|
|
37
|
+
search when you need precise, complete relationships - semantic search is approximate and may miss or over-return \
|
|
38
|
+
results.
|
|
39
|
+
|
|
40
|
+
Use search_in_workspace or search_in_repo_and_branch (prefer the latter when repository/branch is already known - more \
|
|
41
|
+
precise) when you don't know exact file/node names. Treat semantic search as an entry point, then switch to graph \
|
|
42
|
+
tools for precise structured details.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def make_life_span(
|
|
47
|
+
base_url_override: str | None = None, pat_override: str | None = None, workspace_id_override: int | None = None
|
|
48
|
+
):
|
|
49
|
+
@asynccontextmanager
|
|
50
|
+
async def server_lifespan(app: MCPServer) -> AsyncIterator[AppContext]:
|
|
51
|
+
settings = get_settings_with_overrides(
|
|
52
|
+
server_url_override=base_url_override,
|
|
53
|
+
pat_override=pat_override,
|
|
54
|
+
default_workspace_id_override=workspace_id_override,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
yield AppContext(
|
|
58
|
+
api_client=ApiClient(base_url=settings.SERVER_URL, token=settings.PAT.get_secret_value()), settings=settings
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return server_lifespan
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def create_server(
|
|
65
|
+
base_url_override: str | None = None, pat_override: str | None = None, workspace_id_override: int | None = None
|
|
66
|
+
) -> MCPServer:
|
|
67
|
+
"""
|
|
68
|
+
Create an MCPServer instance.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
base_url_override: override default server URL
|
|
72
|
+
pat_override: override default Personal Access Token
|
|
73
|
+
workspace_id_override: override default workspace ID
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
life_span = make_life_span(
|
|
77
|
+
base_url_override=base_url_override, pat_override=pat_override, workspace_id_override=workspace_id_override
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
server = MCPServer(
|
|
81
|
+
name="source-context-mcp",
|
|
82
|
+
lifespan=life_span,
|
|
83
|
+
version=get_package_version(),
|
|
84
|
+
description=SERVER_DESCRIPTION,
|
|
85
|
+
instructions=SERVER_INSTRUCTIONS,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
register_tools(server)
|
|
89
|
+
register_resources(server)
|
|
90
|
+
register_prompts(server)
|
|
91
|
+
|
|
92
|
+
return server
|
{source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/tools/__init__.py
RENAMED
|
@@ -8,7 +8,7 @@ from .vector import register_tools as register_vector_tools
|
|
|
8
8
|
|
|
9
9
|
def register_tools(mcp: MCPServer) -> None:
|
|
10
10
|
"""
|
|
11
|
-
|
|
11
|
+
Register tools for MCPServer.
|
|
12
12
|
Args:
|
|
13
13
|
mcp: instance of MCPServer
|
|
14
14
|
"""
|
|
@@ -17,3 +17,6 @@ def register_tools(mcp: MCPServer) -> None:
|
|
|
17
17
|
register_base_tools(mcp)
|
|
18
18
|
register_graph_tools(mcp)
|
|
19
19
|
register_vector_tools(mcp)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = ["register_tools"]
|
{source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/tools/base.py
RENAMED
|
@@ -1,34 +1,24 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Annotated, Any
|
|
2
2
|
|
|
3
3
|
from mcp.server import MCPServer
|
|
4
4
|
from mcp.server.mcpserver import Context
|
|
5
|
+
from pydantic import Field
|
|
5
6
|
|
|
6
7
|
from source_context_mcp.core import ApiClientDep, AppContext
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def register_tools(mcp: MCPServer) -> None:
|
|
10
|
-
"""
|
|
11
|
-
Register bases tools:
|
|
12
|
-
- list user's available workspaces
|
|
13
|
-
- list repos in workspace
|
|
14
|
-
- list branches in repo
|
|
15
|
-
- list projects in branch
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
mcp: MCPServer instance
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
@mcp.tool(description="List all available workspaces that current user can access")
|
|
12
|
+
@mcp.tool(description="List all workspaces accessible to the current user.")
|
|
22
13
|
async def list_workspaces(client: ApiClientDep) -> Any:
|
|
23
14
|
res = await client.get("/general/workspaces", response_model=list)
|
|
24
15
|
return res.result()
|
|
25
16
|
|
|
26
|
-
@mcp.tool(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
)
|
|
31
|
-
async def get_path_settings(ctx: Context[AppContext], path: str) -> dict[str, Any]:
|
|
17
|
+
@mcp.tool(description="Get default workspace and repository settings configured for a local path.")
|
|
18
|
+
async def get_path_settings(
|
|
19
|
+
ctx: Context[AppContext],
|
|
20
|
+
path: Annotated[str, Field(description="Local directory path to look up default settings for.")],
|
|
21
|
+
) -> dict[str, Any]:
|
|
32
22
|
app_context: AppContext = ctx.request_context.lifespan_context
|
|
33
23
|
settings = app_context.settings
|
|
34
24
|
return {
|
|
@@ -38,25 +28,27 @@ def register_tools(mcp: MCPServer) -> None:
|
|
|
38
28
|
}
|
|
39
29
|
|
|
40
30
|
@mcp.tool(
|
|
41
|
-
description=
|
|
42
|
-
|
|
43
|
-
"This is the default workspace use settings in case current path has no default config"
|
|
44
|
-
)
|
|
31
|
+
description="Get the server-configured default workspace ID, used when the current path has no path-specific "
|
|
32
|
+
"default."
|
|
45
33
|
)
|
|
46
34
|
async def default_workspace(ctx: Context[AppContext]) -> Any:
|
|
47
35
|
app_context: AppContext = ctx.request_context.lifespan_context
|
|
48
36
|
return app_context.settings.DEFAULT_WORKSPACE_ID
|
|
49
37
|
|
|
50
|
-
@mcp.tool(description="List all
|
|
38
|
+
@mcp.tool(description="List all repositories in a workspace.")
|
|
51
39
|
async def list_repositories(
|
|
52
40
|
client: ApiClientDep,
|
|
53
|
-
workspace_id: int,
|
|
41
|
+
workspace_id: Annotated[int, Field(description="Workspace ID.", ge=1)],
|
|
54
42
|
) -> Any:
|
|
55
43
|
res = await client.post("/general/repositories", {"workspace_id": workspace_id})
|
|
56
44
|
return res.result()
|
|
57
45
|
|
|
58
|
-
@mcp.tool(description="List all
|
|
59
|
-
async def list_branches(
|
|
46
|
+
@mcp.tool(description="List all branches in a repository.")
|
|
47
|
+
async def list_branches(
|
|
48
|
+
client: ApiClientDep,
|
|
49
|
+
workspace_id: Annotated[int, Field(description="Workspace ID.", ge=1)],
|
|
50
|
+
repository_id: Annotated[int, Field(description="Repository ID.", ge=1)],
|
|
51
|
+
) -> Any:
|
|
60
52
|
req_body = {
|
|
61
53
|
"workspace_id": workspace_id,
|
|
62
54
|
"repository_id": repository_id,
|
|
@@ -64,8 +56,13 @@ def register_tools(mcp: MCPServer) -> None:
|
|
|
64
56
|
res = await client.post("/general/branches", req_body)
|
|
65
57
|
return res.result()
|
|
66
58
|
|
|
67
|
-
@mcp.tool(description="List all
|
|
68
|
-
async def list_projects(
|
|
59
|
+
@mcp.tool(description="List all projects on a branch.")
|
|
60
|
+
async def list_projects(
|
|
61
|
+
client: ApiClientDep,
|
|
62
|
+
workspace_id: Annotated[int, Field(description="Workspace ID.", ge=1)],
|
|
63
|
+
repository_id: Annotated[int, Field(description="Repository ID.", ge=1)],
|
|
64
|
+
branch_name: Annotated[str, Field(description="Branch name.")],
|
|
65
|
+
) -> Any:
|
|
69
66
|
req_body = {
|
|
70
67
|
"workspace_id": workspace_id,
|
|
71
68
|
"repo_id": repository_id,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from mcp.server import MCPServer
|
|
2
|
+
from mcp.server.mcpserver import Context
|
|
3
|
+
|
|
4
|
+
from source_context_mcp.core import AppContext, Settings
|
|
5
|
+
from source_context_mcp.core.metadata import get_package_version
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register_tools(mcp: MCPServer):
|
|
9
|
+
"""
|
|
10
|
+
Register development-only MCP tools.
|
|
11
|
+
|
|
12
|
+
These tools are registered only when the package version is a
|
|
13
|
+
development release (e.g. ``0.1.0.dev1``). They are excluded from
|
|
14
|
+
alpha, beta, release candidate, and stable builds.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
mcp: The MCP server instance to register tools with.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
version = get_package_version()
|
|
21
|
+
if "dev" not in version:
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
@mcp.tool(description="Show current server configuration. For development use only.")
|
|
25
|
+
def debug(ctx: Context[AppContext]) -> Settings:
|
|
26
|
+
app_context: AppContext = ctx.request_context.lifespan_context
|
|
27
|
+
return app_context.settings
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from typing import Annotated, Any, Literal
|
|
2
|
+
|
|
3
|
+
from mcp.server import MCPServer
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
from source_context_mcp.core import ApiClientDep
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def register_tools(mcp: MCPServer) -> None:
|
|
10
|
+
|
|
11
|
+
@mcp.tool(description="List all files in a project.")
|
|
12
|
+
async def list_files_in_project(
|
|
13
|
+
client: ApiClientDep,
|
|
14
|
+
project_id: Annotated[str, Field(description="Project ID.")],
|
|
15
|
+
) -> Any:
|
|
16
|
+
res = await client.get(f"/graph/projects/{project_id}/files")
|
|
17
|
+
return res.result()
|
|
18
|
+
|
|
19
|
+
@mcp.tool(description="Get file structure: classes and methods.")
|
|
20
|
+
async def get_file_structure(
|
|
21
|
+
client: ApiClientDep,
|
|
22
|
+
file_id: Annotated[str, Field(description="File ID.")],
|
|
23
|
+
) -> Any:
|
|
24
|
+
res = await client.get(f"/graph/files/{file_id}/structure")
|
|
25
|
+
return res.result()
|
|
26
|
+
|
|
27
|
+
@mcp.tool(description="Read file content.")
|
|
28
|
+
async def get_file_content(
|
|
29
|
+
client: ApiClientDep,
|
|
30
|
+
file_id: Annotated[str, Field(description="File ID.")],
|
|
31
|
+
) -> Any:
|
|
32
|
+
res = await client.get(f"/graph/files/{file_id}/content")
|
|
33
|
+
return res.result()
|
|
34
|
+
|
|
35
|
+
@mcp.tool(description="Get node metadata: id, name, file_id, position in file.")
|
|
36
|
+
async def get_node_info(
|
|
37
|
+
client: ApiClientDep,
|
|
38
|
+
node_id: Annotated[str, Field(description="Node ID.")],
|
|
39
|
+
) -> Any:
|
|
40
|
+
res = await client.get(f"/graph/nodes/{node_id}")
|
|
41
|
+
return res.result()
|
|
42
|
+
|
|
43
|
+
@mcp.tool(description="Get node source content.")
|
|
44
|
+
async def get_node_content(
|
|
45
|
+
client: ApiClientDep,
|
|
46
|
+
node_id: Annotated[str, Field(description="Node ID.")],
|
|
47
|
+
) -> Any:
|
|
48
|
+
res = await client.get(f"/graph/nodes/{node_id}/content")
|
|
49
|
+
return res.result()
|
|
50
|
+
|
|
51
|
+
# @mcp.tool(description="This tool not implemented yet.")
|
|
52
|
+
# async def find_node_by_name(
|
|
53
|
+
# client: ApiClientDep,
|
|
54
|
+
# name: Annotated[str, Field(description="The name of the node")],
|
|
55
|
+
# node_type: Annotated[
|
|
56
|
+
# Literal["type-definition", "", None],
|
|
57
|
+
# Field(default=None, description="The type of the node. Default is all types"),
|
|
58
|
+
# ],
|
|
59
|
+
# ) -> Any:
|
|
60
|
+
# return []
|
|
61
|
+
|
|
62
|
+
@mcp.tool(description="Find all nodes that call or reference the specified node.")
|
|
63
|
+
async def find_node_usages(
|
|
64
|
+
client: ApiClientDep,
|
|
65
|
+
node_id: Annotated[str, Field(description="Node ID.")],
|
|
66
|
+
) -> Any:
|
|
67
|
+
res = await client.get(f"/graph/nodes/{node_id}/usages")
|
|
68
|
+
return res.result()
|
|
69
|
+
|
|
70
|
+
@mcp.tool(description="Find all nodes that the specified node calls or references.")
|
|
71
|
+
async def find_node_callees(
|
|
72
|
+
client: ApiClientDep,
|
|
73
|
+
node_id: Annotated[str, Field(description="Node ID.")],
|
|
74
|
+
) -> Any:
|
|
75
|
+
res = await client.get(f"/graph/nodes/{node_id}/callees")
|
|
76
|
+
return res.result()
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from typing import Annotated, Any
|
|
2
|
+
|
|
3
|
+
from mcp.server import MCPServer
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
from source_context_mcp.core import ApiClientDep
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def register_tools(mcp: MCPServer) -> None:
|
|
10
|
+
|
|
11
|
+
@mcp.tool(description="Semantic search across all repositories and branches in a workspace.")
|
|
12
|
+
async def search_in_workspace(
|
|
13
|
+
client: ApiClientDep,
|
|
14
|
+
query: Annotated[str, Field(description="Query to search in workspace.")],
|
|
15
|
+
workspace_id: Annotated[int, Field(ge=1, description="Workspace ID.")],
|
|
16
|
+
top_k: Annotated[int, Field(default=5, ge=1, le=50, description="Number of top results to return.")],
|
|
17
|
+
) -> Any:
|
|
18
|
+
req_body = {
|
|
19
|
+
"query": query,
|
|
20
|
+
"top_k": top_k,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
res = await client.post(f"/vector/search/{workspace_id}", req_body)
|
|
24
|
+
|
|
25
|
+
return res.result()
|
|
26
|
+
|
|
27
|
+
@mcp.tool(description="Semantic search scoped to a specific repository and branch.")
|
|
28
|
+
async def search_in_repo_and_branch(
|
|
29
|
+
client: ApiClientDep,
|
|
30
|
+
repository_id: Annotated[int, Field(ge=1, description="Repository ID.")],
|
|
31
|
+
branch_name: Annotated[str, Field(description="Branch name.")],
|
|
32
|
+
query: Annotated[str, Field(description="Query to search in branch scope.")],
|
|
33
|
+
top_k: Annotated[int, Field(default=5, ge=1, le=50, description="Number of top results to return.")],
|
|
34
|
+
) -> Any:
|
|
35
|
+
req_body = {
|
|
36
|
+
"query": query,
|
|
37
|
+
"top_k": top_k,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
res = await client.post(f"/vector/search/{repository_id}/{branch_name}", req_body)
|
|
41
|
+
|
|
42
|
+
return res.result()
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: source-context-mcp
|
|
3
|
-
Version: 0.1.dev3
|
|
4
|
-
Summary: Local Model Context Protocol (MCP) server for Source Context code intelligence.
|
|
5
|
-
Author: Le Ngoc Tu, Dang Huu Hieu, Huynh Phat Tai
|
|
6
|
-
Author-email: Le Ngoc Tu <tuleaibk@gmail.com>, Dang Huu Hieu <danghuuhieu038@gmail.com>, Huynh Phat Tai <huynhphtai@gmail.com>
|
|
7
|
-
Requires-Dist: httpx>=0.28.1
|
|
8
|
-
Requires-Dist: mcp[cli]>=2.0.0
|
|
9
|
-
Requires-Dist: pydantic>=2.13.4
|
|
10
|
-
Requires-Dist: pydantic-settings>=2.14.2
|
|
11
|
-
Requires-Dist: tomli-w>=1.2.0
|
|
12
|
-
Requires-Dist: typer>=0.27.0
|
|
13
|
-
Requires-Python: >=3.12
|
|
14
|
-
Project-URL: Repository, https://github.com/TuLe142857/source-context
|
|
15
|
-
Description-Content-Type: text/markdown
|
|
16
|
-
|
|
17
|
-
# Readme for MCP Server
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# Readme for MCP Server
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
from collections.abc import AsyncIterator
|
|
2
|
-
from contextlib import asynccontextmanager
|
|
3
|
-
|
|
4
|
-
from mcp.server import MCPServer
|
|
5
|
-
|
|
6
|
-
from source_context_mcp.core import ApiClient, AppContext, get_settings_with_overrides
|
|
7
|
-
from source_context_mcp.tools import register_tools
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def make_life_span(
|
|
11
|
-
base_url_override: str | None = None, pat_override: str | None = None, workspace_id_override: int | None = None
|
|
12
|
-
):
|
|
13
|
-
@asynccontextmanager
|
|
14
|
-
async def server_lifespan(app: MCPServer) -> AsyncIterator[AppContext]:
|
|
15
|
-
settings = get_settings_with_overrides(
|
|
16
|
-
server_url_override=base_url_override,
|
|
17
|
-
pat_override=pat_override,
|
|
18
|
-
default_workspace_id_override=workspace_id_override,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
yield AppContext(
|
|
22
|
-
api_client=ApiClient(base_url=settings.SERVER_URL, token=settings.PAT.get_secret_value()), settings=settings
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
return server_lifespan
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def create_server(
|
|
29
|
-
base_url_override: str | None = None, pat_override: str | None = None, workspace_id_override: int | None = None
|
|
30
|
-
) -> MCPServer:
|
|
31
|
-
"""
|
|
32
|
-
Create an MCPServer instance.
|
|
33
|
-
Returns:
|
|
34
|
-
MCPServer instance.
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
life_span = make_life_span(
|
|
38
|
-
base_url_override=base_url_override, pat_override=pat_override, workspace_id_override=workspace_id_override
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
server = MCPServer(
|
|
42
|
-
name="SourceContextMCP",
|
|
43
|
-
lifespan=life_span,
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
register_tools(server)
|
|
47
|
-
|
|
48
|
-
return server
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
from mcp.server import MCPServer
|
|
2
|
-
from mcp.server.mcpserver import Context
|
|
3
|
-
|
|
4
|
-
from source_context_mcp.core import AppContext, Settings
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def register_tools(mcp: MCPServer):
|
|
8
|
-
@mcp.tool(description="This tool for development env only. Show all current config. Read source code to see more")
|
|
9
|
-
def debug(ctx: Context[AppContext]) -> Settings:
|
|
10
|
-
app_context: AppContext = ctx.request_context.lifespan_context
|
|
11
|
-
return app_context.settings
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
|
-
from mcp.server import MCPServer
|
|
4
|
-
|
|
5
|
-
from source_context_mcp.core import ApiClientDep
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def register_tools(mcp: MCPServer) -> None:
|
|
9
|
-
"""
|
|
10
|
-
Registers all the graph tools.
|
|
11
|
-
Args:
|
|
12
|
-
mcp: MCPServer instance
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
@mcp.tool(description="List all files in specified project.")
|
|
16
|
-
async def list_files_in_project(client: ApiClientDep, project_id: str) -> Any:
|
|
17
|
-
res = await client.get(f"/graph/projects/{project_id}/files")
|
|
18
|
-
return res.result()
|
|
19
|
-
|
|
20
|
-
@mcp.tool(description="Inspect file structure(classes and methods")
|
|
21
|
-
async def get_file_structure(client: ApiClientDep, file_id: str) -> Any:
|
|
22
|
-
res = await client.get(f"/graph/files/{file_id}/structure")
|
|
23
|
-
return res.result()
|
|
24
|
-
|
|
25
|
-
@mcp.tool(description="Read file content.")
|
|
26
|
-
async def get_file_content(client: ApiClientDep, file_id: str) -> Any:
|
|
27
|
-
res = await client.get(f"/graph/files/{file_id}/content")
|
|
28
|
-
return res.result()
|
|
29
|
-
|
|
30
|
-
@mcp.tool(description="Get node information: id, name, file_id, position in file")
|
|
31
|
-
async def get_node_info(client: ApiClientDep, node_id: str) -> Any:
|
|
32
|
-
res = await client.get(f"/graph/nodes/{node_id}")
|
|
33
|
-
return res.result()
|
|
34
|
-
|
|
35
|
-
@mcp.tool(description="Get node information.")
|
|
36
|
-
async def get_node_content(client: ApiClientDep, node_id: str) -> Any:
|
|
37
|
-
res = await client.get(f"/graph/nodes/{node_id}/content")
|
|
38
|
-
return res.result()
|
|
39
|
-
|
|
40
|
-
@mcp.tool(description="Find all nodes that call/reference to the specified node.")
|
|
41
|
-
async def find_node_usages(client: ApiClientDep, node_id: str) -> Any:
|
|
42
|
-
res = await client.get(f"/graph/nodes/{node_id}/usages")
|
|
43
|
-
return res.result()
|
|
44
|
-
|
|
45
|
-
@mcp.tool(description="Find all nodes(or its children) that the specified node is calling or reference to.")
|
|
46
|
-
async def find_node_callees(client: ApiClientDep, node_id: str) -> Any:
|
|
47
|
-
res = await client.get(f"/graph/nodes/{node_id}/callees")
|
|
48
|
-
return res.result()
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
|
-
from mcp.server import MCPServer
|
|
4
|
-
|
|
5
|
-
from source_context_mcp.core import ApiClientDep
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def register_tools(mcp: MCPServer) -> None:
|
|
9
|
-
"""
|
|
10
|
-
Registers all the vector tools.
|
|
11
|
-
Args:
|
|
12
|
-
mcp: MCP server instance
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
@mcp.tool(description="Sematic search.")
|
|
16
|
-
async def search_in_workspace(client: ApiClientDep, query: str, workspace_id: int, top_k: int = 5) -> Any:
|
|
17
|
-
req_body = {
|
|
18
|
-
"query": query,
|
|
19
|
-
"top_k": top_k,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
res = await client.post(f"/vector/search/{workspace_id}", req_body)
|
|
23
|
-
|
|
24
|
-
return res.result()
|
|
25
|
-
|
|
26
|
-
@mcp.tool(description="Sematic search.")
|
|
27
|
-
async def search_in_repo_and_branch(
|
|
28
|
-
client: ApiClientDep, repository_id: int, branch_name: str, query: str, top_k: int = 5
|
|
29
|
-
) -> Any:
|
|
30
|
-
req_body = {
|
|
31
|
-
"query": query,
|
|
32
|
-
"top_k": top_k,
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
res = await client.post(f"/vector/search/{repository_id}/{branch_name}", req_body)
|
|
36
|
-
|
|
37
|
-
return res.result()
|
{source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/__init__.py
RENAMED
|
File without changes
|
{source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/__main__.py
RENAMED
|
File without changes
|
{source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/core/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{source_context_mcp-0.1.dev3 → source_context_mcp-0.1.dev5}/src/source_context_mcp/core/context.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|