@latentforce/shift 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +121 -0
  2. package/build/index.js +132 -0
  3. package/package.json +46 -0
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # shift-mcp
2
+
3
+ A minimal utility server built for the Model Context Protocol (MCP). It exposes tools that call the Shift Lite webapp backend for file summary, dependencies, and blast radius (using the project's knowledge graph).
4
+
5
+ ## Environment variables
6
+
7
+ - **`SHIFT_PROJECT_ID`** (optional if you pass per call): The default Shift Lite project UUID. If set, all tools use this project unless overridden by the `project_id` parameter in a tool call.
8
+ - Example: `export SHIFT_PROJECT_ID=9af16a19-d073-4134-a0cd-272b0baf912e`
9
+ - **`SHIFT_BACKEND_URL`** (optional): Webapp backend base URL. Default: `http://127.0.0.1:9000`.
10
+
11
+ ## Project ID: env vs per-call
12
+
13
+ - **Default:** Set `SHIFT_PROJECT_ID` in the MCP config (or env). All tools use that project.
14
+ - **Override per call:** Each tool (`blast_radius`, `dependencies`, `file_summary`) accepts an optional **`project_id`** parameter. If you pass it, that call uses that project; otherwise the tool uses `SHIFT_PROJECT_ID`. So you can use one MCP for multiple projects by passing `project_id` in the call (e.g. "blast radius for project abc-123").
15
+
16
+ ## Installation
17
+
18
+ ### Option 1: Install globally (simple, persistent)
19
+
20
+ ```bash
21
+ npm install -g @latentforce/shift
22
+ ```
23
+
24
+ Verify it works:
25
+ ```bash
26
+ shift
27
+ ```
28
+
29
+ You should see:
30
+ ```bash
31
+ Shift-lite MCP Server running on stdio
32
+ ```
33
+
34
+
35
+ ## Claude Code setup
36
+
37
+ Claude Code does not auto-discover MCP servers. You must register it manually.
38
+
39
+ ### Install the Package (if not already)
40
+ You can use this package without installing it globally by using npx, or install it globally for faster access.
41
+
42
+ ```bash
43
+ npm install -g @latentforce/shift
44
+ ```
45
+
46
+ ### Add to Claude Code
47
+ - Replace SHIFT_BACKEND_URL and SHIFT_PROJECT_ID with correct values.
48
+ - SHIFT_PROJECT_ID may be ignored as every tool accepts an optional **`project_id`** parameter.
49
+ For Windows Users
50
+ ```bash
51
+ claude mcp add --scope user --transport stdio --env SHIFT_BACKEND_URL=BACKEND_URL --env SHIFT_PROJECT_ID=9af16a19-d073-4134-a0cd-272b0baf912e shift -- cmd /c npx -y @latentforce/shift
52
+ ```
53
+
54
+ For macOS/Linux Users
55
+ ```bash
56
+ claude mcp add --scope user --transport stdio --env SHIFT_BACKEND_URL=BACKEND_URL --env SHIFT_PROJECT_ID=9af16a19-d073-4134-a0cd-272b0baf912e shift -- npx -y @latentforce/shift
57
+ ```
58
+
59
+ ### Verify Installation
60
+ ```bash
61
+ claude mcp list
62
+ ```
63
+
64
+ ### Use It
65
+ Start a Claude Code session:
66
+ ```bash
67
+ claude
68
+ ```
69
+
70
+ ## Claude Desktop setup
71
+
72
+ Claude Desktop does not auto-discover MCP servers. You must register it manually.
73
+
74
+ ### Config file location
75
+
76
+ **macOS (Claude Desktop app):**
77
+ ```bash
78
+ ~/Library/Application Support/Claude/claude_desktop_config.json
79
+ ```
80
+ *(In Claude Desktop: Claude menu → Settings… → Developer → Edit Config)*
81
+
82
+ **Linux (if different):**
83
+ ```bash
84
+ ~/.config/claude-desktop/claude_desktop_config.json
85
+ ```
86
+
87
+ **Windows:**
88
+ ```bash
89
+ %APPDATA%\Claude\claude_desktop_config.json
90
+ ```
91
+
92
+ Add this (replace `YOUR_PROJECT_UUID` with your Shift Lite project ID):
93
+ ```json
94
+ {
95
+ "mcpServers": {
96
+ "shift": {
97
+ "command": "shift",
98
+ "env": {
99
+ "SHIFT_BACKEND_URL": "BACKEND URL",
100
+ "SHIFT_PROJECT_ID": "YOUR_PROJECT_UUID"
101
+ }
102
+ }
103
+ }
104
+ }
105
+ ```
106
+
107
+ Restart Claude Desktop after saving.
108
+
109
+ ## Development
110
+
111
+ Clone and build:
112
+ ```bash
113
+ npm install
114
+ npm run build
115
+ ```
116
+
117
+ Run locally:
118
+ ```bash
119
+ node build/index.js
120
+ ```
121
+
package/build/index.js ADDED
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from 'zod';
5
+ // Create server instance
6
+ const server = new McpServer({
7
+ name: "shift",
8
+ version: "1.0.0",
9
+ });
10
+ const BASE_URL = process.env.SHIFT_BACKEND_URL || "http://127.0.0.1:9000";
11
+ function getProjectIdFromEnv() {
12
+ const projectId = process.env.SHIFT_PROJECT_ID;
13
+ if (!projectId || projectId.trim() === "") {
14
+ throw new Error("SHIFT_PROJECT_ID environment variable is not set. " +
15
+ "Set it to your Shift Lite project UUID, or pass project_id in each tool call.");
16
+ }
17
+ return projectId.trim();
18
+ }
19
+ /** Resolve project_id: use tool arg if provided, else fall back to env. */
20
+ function resolveProjectId(args) {
21
+ const fromArgs = args.project_id?.trim();
22
+ if (fromArgs)
23
+ return fromArgs;
24
+ return getProjectIdFromEnv();
25
+ }
26
+ // helper
27
+ async function callBackendAPI(endpoint, data) {
28
+ try {
29
+ const response = await fetch(`${BASE_URL}${endpoint}`, {
30
+ method: 'POST',
31
+ headers: {
32
+ 'Content-Type': 'application/json',
33
+ },
34
+ body: JSON.stringify(data),
35
+ });
36
+ if (!response.ok) {
37
+ const text = await response.text();
38
+ throw new Error(`API call failed: ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`);
39
+ }
40
+ return await response.json();
41
+ }
42
+ catch (error) {
43
+ console.error(`Error calling ${endpoint}:`, error);
44
+ throw error;
45
+ }
46
+ }
47
+ // Tools:
48
+ // Blast radius
49
+ server.registerTool("blast_radius", {
50
+ description: "Analyzes the blast radius of a file or component - shows what would be affected if this file were modified or deleted",
51
+ inputSchema: z.object({
52
+ file_path: z.string().describe("Path to the file (relative to project root)"),
53
+ project_id: z.string().optional().describe("Shift Lite project UUID; overrides SHIFT_PROJECT_ID if provided"),
54
+ project_path: z.string().optional().describe("Path to the root of the project (optional)"),
55
+ level: z.number().optional().describe("Max depth of blast radius (optional)"),
56
+ })
57
+ }, async (args) => {
58
+ const projectId = resolveProjectId(args);
59
+ const data = await callBackendAPI('/api/v1/mcp/blast-radius', {
60
+ path: args.file_path,
61
+ project_id: projectId,
62
+ ...(args.level != null && { level: args.level }),
63
+ });
64
+ return {
65
+ content: [
66
+ {
67
+ type: "text",
68
+ text: JSON.stringify(data, null, 2)
69
+ },
70
+ ],
71
+ };
72
+ });
73
+ // Dependencies
74
+ server.registerTool("dependencies", {
75
+ description: "Retrieves all dependencies for a given file or component, including direct and transitive dependencies",
76
+ inputSchema: z.object({
77
+ file_path: z.string().describe("Path to the file"),
78
+ project_id: z.string().optional().describe("Shift Lite project UUID; overrides SHIFT_PROJECT_ID if provided"),
79
+ project_path: z.string().optional().describe("Path to the root of the project (optional)"),
80
+ })
81
+ }, async (args) => {
82
+ const projectId = resolveProjectId(args);
83
+ const data = await callBackendAPI('/api/v1/mcp/dependency', {
84
+ path: args.file_path,
85
+ project_id: projectId,
86
+ });
87
+ return {
88
+ content: [
89
+ {
90
+ type: "text",
91
+ text: JSON.stringify(data, null, 2)
92
+ },
93
+ ],
94
+ };
95
+ });
96
+ // File summary (maps to what-is-this-file)
97
+ server.registerTool("file_summary", {
98
+ description: "Generates a comprehensive summary of a file including its purpose, exports, imports, and key functions, with optional parent directory context",
99
+ inputSchema: z.object({
100
+ file_path: z.string().describe("Path to the file to summarize"),
101
+ project_id: z.string().optional().describe("Shift Lite project UUID; overrides SHIFT_PROJECT_ID if provided"),
102
+ project_path: z.string().optional().describe("Path to the root of the project (optional)"),
103
+ level: z.number().optional().describe("Number of parent directory levels to include (default 0)"),
104
+ })
105
+ }, async (args) => {
106
+ const projectId = resolveProjectId(args);
107
+ const data = await callBackendAPI('/api/v1/mcp/what-is-this-file', {
108
+ path: args.file_path,
109
+ project_id: projectId,
110
+ level: args.level ?? 0,
111
+ });
112
+ return {
113
+ content: [
114
+ {
115
+ type: "text",
116
+ text: JSON.stringify(data, null, 2)
117
+ },
118
+ ],
119
+ };
120
+ });
121
+ async function main() {
122
+ const transport = new StdioServerTransport();
123
+ await server.connect(transport);
124
+ console.error("Shift MCP Server running on stdio");
125
+ if (!process.env.SHIFT_PROJECT_ID) {
126
+ console.error("Warning: SHIFT_PROJECT_ID is not set. Pass project_id in each tool call, or set the env var.");
127
+ }
128
+ }
129
+ main().catch((error) => {
130
+ console.error("Fatal error in main():", error);
131
+ process.exit(1);
132
+ });
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@latentforce/shift",
3
+ "version": "1.0.0",
4
+ "description": "This is latenforce shift MCP",
5
+ "type": "module",
6
+ "main": "./build/index.js",
7
+ "exports": {
8
+ ".": "./build/index.js"
9
+ },
10
+ "bin": {
11
+ "shift": "./build/index.js"
12
+ },
13
+ "files": [
14
+ "build"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "prepublishOnly": "npm run build",
19
+ "test": "echo \"No tests yet\""
20
+ },
21
+ "keywords": [
22
+ "mcp",
23
+ "model-context-protocol",
24
+ "cli",
25
+ "node",
26
+ "typescript"
27
+ ],
28
+ "author": "Latentforce",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": ""
33
+ },
34
+ "homepage": "",
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.25.3",
40
+ "zod": "^3.25.76"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^25.0.9",
44
+ "typescript": "^5.9.3"
45
+ }
46
+ }