Skip to content

Commit bffe36c

Browse files
authored
fix: kiro integration (#24)
1 parent ef02820 commit bffe36c

File tree

6 files changed

+1293
-87
lines changed

6 files changed

+1293
-87
lines changed

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
node_modules
2-
*.log
1+
node_modules/
2+
*.log
3+
.kiro/
4+
.vscode/

README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,25 @@ npm install -g @rkristelijn/lcode
4747

4848
# Or use with npx (no installation)
4949
npx @rkristelijn/lcode
50+
```
51+
52+
### MCP Integration (AI Assistants)
53+
54+
lcode provides native Model Context Protocol (MCP) support for AI assistants like Kiro CLI:
5055

51-
# Kiro CLI MCP Integration
52-
kiro-cli mcp add --name "lcode" --command "npx" --args "@rkristelijn/lcode"
56+
```bash
57+
# After global installation, add to ~/.kiro/settings/mcp.json
58+
{
59+
"mcpServers": {
60+
"lcode": {
61+
"command": "lcode-mcp",
62+
"args": []
63+
}
64+
}
65+
}
5366
```
5467

55-
📖 [Kiro CLI MCP Integration Guide](docs/mcp-integration.md)
68+
📖 [Full MCP Integration Guide](docs/mcp-integration.md)
5669

5770
### Basic Usage
5871

docs/mcp-integration.md

Lines changed: 115 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,160 @@
11
# lcode MCP Integration for Kiro CLI
22

3+
lcode provides native Model Context Protocol (MCP) support, allowing AI assistants like Kiro to discover and interact with your repositories through structured tools.
4+
35
## Quick Setup
46

5-
Add lcode as an MCP tool to Kiro CLI:
7+
### Global Installation (Recommended)
8+
9+
Install lcode globally and configure the MCP server:
610

711
```bash
8-
kiro-cli mcp add \
9-
--name "lcode" \
10-
--scope global \
11-
--command "npx" \
12-
--args "@rkristelijn/lcode"
12+
# Install lcode globally
13+
npm install -g @rkristelijn/lcode
14+
15+
# Add to Kiro CLI MCP configuration
16+
# Create or edit ~/.kiro/settings/mcp.json
17+
{
18+
"mcpServers": {
19+
"lcode": {
20+
"command": "lcode-mcp",
21+
"args": [],
22+
"env": {}
23+
}
24+
}
25+
}
1326
```
1427

15-
Or manually add to `~/.kiro/settings/mcp.json`:
28+
### Local Development Setup
1629

17-
```json
30+
For local development or testing:
31+
32+
```bash
33+
# Clone and install
34+
git clone https://github.com/rkristelijn/lcode.git
35+
cd lcode
36+
npm install
37+
38+
# Configure MCP to use local server
39+
# Edit .kiro/settings/mcp.json in your project
1840
{
1941
"mcpServers": {
2042
"lcode": {
21-
"command": "npx",
22-
"args": ["@rkristelijn/lcode"],
23-
"disabled": false
43+
"command": "node",
44+
"args": ["/path/to/lcode/mcp-server.mjs"],
45+
"env": {}
2446
}
2547
}
2648
}
2749
```
2850

29-
## Usage with Kiro
51+
## MCP Tools
3052

31-
Once configured, you can use lcode directly in Kiro chat:
53+
The lcode MCP server exposes two tools:
3254

33-
```
34-
"List all TypeScript repositories in ~/git/hub"
35-
→ Uses: npx @rkristelijn/lcode ~/git/hub 2 --list --lang ts
55+
### `list_repos`
56+
Lists all git repositories with language detection and filtering.
3657

37-
"Show me all Python projects"
38-
→ Uses: npx @rkristelijn/lcode ~ 3 --list --lang python
58+
**Parameters:**
59+
- `path` (optional) - Directory to search (default: from config)
60+
- `maxDepth` (optional) - Search depth 1-10 (default: from config)
61+
- `language` (optional) - Filter by language(s), comma-separated (e.g., "ts,js")
3962

40-
"Find Java or Kotlin projects"
41-
→ Uses: npx @rkristelijn/lcode ~ 3 --list --lang java,kotlin
63+
**Returns:** Array of repositories with index, name, path, languages, and description.
4264

43-
"Open the first TypeScript project in VS Code"
44-
→ Uses: npx @rkristelijn/lcode ~ 3 --lang ts --select 0 "code ."
45-
```
65+
### `select_repo`
66+
Gets detailed information about a specific repository.
4667

47-
## Available Commands
68+
**Parameters:**
69+
- `index` (optional) - Repository index from list_repos
70+
- `name` (optional) - Repository name
71+
- `path` (optional) - Search path (default: from config)
72+
- `maxDepth` (optional) - Search depth (default: from config)
4873

49-
All lcode commands work through npx:
74+
**Returns:** Repository details including full path and metadata.
5075

51-
```bash
52-
# List repositories
53-
npx @rkristelijn/lcode ~/git/hub 2 --list
76+
## Usage with Kiro
5477

55-
# Filter by language
56-
npx @rkristelijn/lcode ~/git/hub 2 --list --lang ts
78+
Once configured, Kiro can use lcode tools naturally:
5779

58-
# Multiple languages
59-
npx @rkristelijn/lcode ~/git/hub 2 --list --lang ts,js
80+
```
81+
User: "Find me 4 TypeScript repos"
82+
Kiro: [Uses list_repos with language="ts"]
6083
61-
# Select and open
62-
npx @rkristelijn/lcode ~/git/hub 2 --select 0 "code ."
84+
User: "Show all Python projects"
85+
Kiro: [Uses list_repos with language="python"]
6386
64-
# Help
65-
npx @rkristelijn/lcode --help
87+
User: "List Java or Kotlin projects"
88+
Kiro: [Uses list_repos with language="java,kotlin"]
89+
90+
User: "Get details about the first repo"
91+
Kiro: [Uses select_repo with index=0]
6692
```
6793

6894
## Benefits
6995

70-
- ✅ No installation required (uses npx)
71-
- ✅ Always uses latest version
72-
- ✅ Works with all lcode features
96+
- ✅ Native MCP protocol support
97+
- ✅ Structured tool interface for AI assistants
7398
- ✅ Language filtering built-in
74-
- ✅ Fast repository discovery
99+
- ✅ Fast repository discovery with caching
100+
- ✅ Works with any MCP-compatible AI tool
101+
- ✅ Type-safe tool definitions
75102

76103
## Example Workflows
77104

78-
### Find and open a project
105+
### Find TypeScript projects
79106
```
80-
User: "Show me all my TypeScript projects"
81-
Kiro: [Lists TypeScript repos with indices]
82-
User: "Open the second one"
83-
Kiro: [Executes: npx @rkristelijn/lcode --select 1 --lang ts]
107+
User: "Find me TypeScript repositories"
108+
→ Kiro calls: list_repos({ language: "ts" })
109+
→ Returns: Structured list with indices, paths, and descriptions
84110
```
85111

86-
### Language-specific search
112+
### Multi-language search
87113
```
88-
User: "Find all Python projects in my home directory"
89-
Kiro: [Executes: npx @rkristelijn/lcode ~ 3 --list --lang python]
114+
User: "Show me Java or Kotlin projects"
115+
→ Kiro calls: list_repos({ language: "java,kotlin" })
116+
→ Returns: Filtered list of matching repositories
90117
```
91118

92-
### Multi-language filtering
119+
### Get repository details
93120
```
94-
User: "Show me Java or Kotlin projects"
95-
Kiro: [Executes: npx @rkristelijn/lcode ~ 3 --list --lang java,kotlin]
121+
User: "Tell me about repo #5"
122+
→ Kiro calls: select_repo({ index: 5 })
123+
→ Returns: Full details including path and metadata
124+
```
125+
126+
## Configuration
127+
128+
lcode uses `~/.lcodeconfig` for default settings:
129+
130+
```json
131+
{
132+
"path": "~",
133+
"maxDepth": 5,
134+
"execute": "code .",
135+
"execute2": "zsh"
136+
}
96137
```
138+
139+
The MCP server respects these defaults when parameters are not provided.
140+
141+
## Troubleshooting
142+
143+
### MCP server not loading
144+
```bash
145+
# Test the MCP server directly
146+
node mcp-server.mjs
147+
148+
# Check Kiro MCP status
149+
kiro-cli mcp list
150+
```
151+
152+
### Tools not appearing
153+
- Restart Kiro CLI after updating mcp.json
154+
- Verify lcode-mcp is in your PATH (for global install)
155+
- Check node version (requires Node.js 16+)
156+
157+
### Language filtering not working
158+
- Use lowercase language codes: "ts", "python", "java"
159+
- Multiple languages: "ts,js" (comma-separated, no spaces)
160+
- Check supported languages in README.md

mcp-server.mjs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/usr/bin/env node
2+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
5+
import { glob } from 'glob';
6+
import path from 'path';
7+
import fs from 'fs';
8+
import { expandHomeDir, isGitRepo, detectLanguages, getReadmePreview } from './src/utils.mjs';
9+
import { RepoCache } from './src/cache.mjs';
10+
11+
const cache = new RepoCache();
12+
const configPath = path.resolve(process.env.HOME, '.lcodeconfig');
13+
14+
const getConfig = () => {
15+
try {
16+
if (fs.existsSync(configPath)) {
17+
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
18+
}
19+
} catch {}
20+
return { path: '~', maxDepth: 5 };
21+
};
22+
23+
const findRepos = async (searchPath, maxDepth, langFilter = null) => {
24+
const expanded = expandHomeDir(searchPath);
25+
const cacheKey = `${expanded}-${maxDepth}`;
26+
27+
let repos = cache.get(cacheKey);
28+
if (!repos) {
29+
const pattern = `${expanded}/${'*/'.repeat(maxDepth - 1)}`;
30+
const folders = await glob(pattern, {
31+
ignore: ['**/node_modules/**', '**/build/**', '**/dist/**', '**/.git/**'],
32+
absolute: true
33+
});
34+
35+
repos = folders
36+
.filter(isGitRepo)
37+
.map(repoPath => ({
38+
path: repoPath,
39+
name: path.basename(repoPath),
40+
languages: detectLanguages(repoPath),
41+
description: getReadmePreview(repoPath)
42+
}));
43+
44+
cache.set(cacheKey, repos);
45+
}
46+
47+
if (langFilter) {
48+
const filters = langFilter.split(',').map(l => l.trim().toLowerCase());
49+
repos = repos.filter(r => r.languages.some(l => filters.includes(l.toLowerCase())));
50+
}
51+
52+
return repos;
53+
};
54+
55+
const server = new Server(
56+
{ name: 'lcode', version: '1.0.0' },
57+
{ capabilities: { tools: {} } }
58+
);
59+
60+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
61+
tools: [
62+
{
63+
name: 'list_repos',
64+
description: 'List all git repositories in a directory with language detection',
65+
inputSchema: {
66+
type: 'object',
67+
properties: {
68+
path: { type: 'string', description: 'Directory to search (default: from config)' },
69+
maxDepth: { type: 'number', description: 'Search depth 1-10 (default: from config)' },
70+
language: { type: 'string', description: 'Filter by language (comma-separated: ts,js,python,etc)' }
71+
}
72+
}
73+
},
74+
{
75+
name: 'select_repo',
76+
description: 'Get details about a specific repository by index or name',
77+
inputSchema: {
78+
type: 'object',
79+
properties: {
80+
index: { type: 'number', description: 'Repository index from list_repos' },
81+
name: { type: 'string', description: 'Repository name' },
82+
path: { type: 'string', description: 'Search path (default: from config)' },
83+
maxDepth: { type: 'number', description: 'Search depth (default: from config)' }
84+
}
85+
}
86+
}
87+
]
88+
}));
89+
90+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
91+
const { name, arguments: args } = request.params;
92+
const config = getConfig();
93+
94+
if (name === 'list_repos') {
95+
const searchPath = args.path || config.path || '~';
96+
const maxDepth = args.maxDepth || config.maxDepth || 5;
97+
const repos = await findRepos(searchPath, maxDepth, args.language);
98+
99+
return {
100+
content: [{
101+
type: 'text',
102+
text: JSON.stringify(repos.map((r, i) => ({
103+
index: i,
104+
name: r.name,
105+
path: r.path,
106+
languages: r.languages,
107+
description: r.description
108+
})), null, 2)
109+
}]
110+
};
111+
}
112+
113+
if (name === 'select_repo') {
114+
const searchPath = args.path || config.path || '~';
115+
const maxDepth = args.maxDepth || config.maxDepth || 5;
116+
const repos = await findRepos(searchPath, maxDepth);
117+
118+
let repo;
119+
if (args.index !== undefined) {
120+
repo = repos[args.index];
121+
} else if (args.name) {
122+
repo = repos.find(r => r.name === args.name);
123+
}
124+
125+
if (!repo) {
126+
return { content: [{ type: 'text', text: 'Repository not found' }], isError: true };
127+
}
128+
129+
return {
130+
content: [{
131+
type: 'text',
132+
text: JSON.stringify(repo, null, 2)
133+
}]
134+
};
135+
}
136+
137+
return { content: [{ type: 'text', text: 'Unknown tool' }], isError: true };
138+
});
139+
140+
const transport = new StdioServerTransport();
141+
await server.connect(transport);

0 commit comments

Comments
 (0)