lsp-extract-function — Claude Code skill: install and usage
Extracts selected code blocks into named functions via LSP code actions or manual AST fallback. Validates scopes, arguments, and compilation diagnostics.
npx skills add blackwell-systems/agent-lsp --skill lsp-extract-function -a claude-codeWhat lsp-extract-function does
This skill restructures code in existing projects. You specify an absolute file path, line numbers for the selection, and the target function name. The skill opens the document and fetches existing symbols. If a function with that name already exists, the process stops immediately to prevent collisions.
The primary workflow calls suggest_fixes to look for a refactor.extract code action from the language server. When the server lacks this capability, the skill falls back to manual analysis. It calculates captured outer variables for arguments and tracks values that must be returned. It presents the proposed function signature to the developer for confirmation. Edits happen sequentially: call-site replacement comes first to avoid shifting line offsets.
Once the code is inserted, the skill pulls diagnostics via get_diagnostics to detect shadowing or missing types, then formats the file. Without the agent-lsp MCP server, this skill cannot run.
What it helps with
- extract code blocks into standalone functions without breaking scopes
- prevent naming collisions before applying refactoring edits
- fallback to manual parameter analysis when language servers lack extract actions
- catch compiler diagnostic errors right after function extraction
Example prompts for the agent
- Extract lines 40 to 75 in
/workspace/src/auth.tsinto a new function namedverifyToken - Refactor lines 12 through 35 in
/project/pkg/parser.gointoparseHeaders - Move lines 100-142 from
/app/main.pyinto a new function calledprocess_batch
How to install lsp-extract-function
1Skills CLI (Vercel)
npx skills add blackwell-systems/agent-lsp --skill lsp-extract-function -a claude-code2Manually: put the files in place
~/.claude/skills/lsp-extract-function/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/lsp-extract-function/
1Skills CLI (Vercel)
npx skills add blackwell-systems/agent-lsp --skill lsp-extract-function -a codex2Manually: put the files in place
.agents/skills/lsp-extract-function/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add blackwell-systems/agent-lsp --skill lsp-extract-function -a cursor2Manually: put the files in place
.cursor/skills/lsp-extract-function/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install blackwell-systems/agent-lsp lsp-extract-function2Manually: put the files in place
.github/skills/lsp-extract-function/SKILL.md1Skills CLI (Vercel)
npx skills add blackwell-systems/agent-lsp --skill lsp-extract-function -a gemini-cli2Platform CLI
gemini skills install https://github.com/blackwell-systems/agent-lsp --path skills/lsp-extract-functionManually: put the files in place
.devin/skills/lsp-extract-function/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add blackwell-systems/agent-lsp --skill lsp-extract-function -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about lsp-extract-function
How to install lsp-extract-function in Claude Code?
Run npx skills add blackwell-systems/agent-lsp --skill lsp-extract-function -a claude-code.
What does lsp-extract-function do?
It takes a line range in an existing file and turns it into a separate function, checking variable scope and running compiler diagnostics.
What if the language server does not have an extract-function code action?
The skill triggers a manual fallback that inspects captured variables, proposes a signature for confirmation, and applies the edits sequentially.
Source text
SKILL.md · MIT
---
name: lsp-extract-function
description: Extract a selected code block into a named function. Primary path uses the language server's extract-function code action; falls back to manual extraction when no code action is available. Validates captured variables, scope shadowing, and compilation after extraction.
argument-hint: "[file-path] [start-line] [end-line] [new-function-name]"
user-invocable: true
allowed-tools: mcp__lsp__list_symbols mcp__lsp__suggest_fixes mcp__lsp__execute_command mcp__lsp__apply_edit mcp__lsp__get_diagnostics mcp__lsp__open_document mcp__lsp__format_document mcp__lsp__get_server_capabilities
license: MIT
compatibility: Requires the agent-lsp MCP server (github.com/blackwell-systems/agent-lsp)
metadata:
required-capabilities: codeActionProvider
optional-capabilities: documentFormattingProvider documentSymbolProvider
---
> Requires the agent-lsp MCP server.
# lsp-extract-function: Extract Code Block into a Named Function
**This skill RESTRUCTURES existing code** — it takes code that already exists
and moves it into a new function. This is distinct from `/lsp-generate`, which
creates NEW code that does not yet exist (stubs, mocks, interface implementations).
Use this skill when the code is already written; use `/lsp-generate` when you
need to generate code from scratch.
**Invocation:** User provides `file_path` (absolute path), `start_line` and
`end_line` (1-indexed range), and `new_function_name` (desired name for the
extracted function).
---
## Prerequisites
If LSP is not yet initialized, call `mcp__lsp__start_lsp` with the workspace
root first. Auto-inference applies when file paths are provided, but an explicit
start is required when switching workspaces.
---
## Step 1 — Get context (document symbols)
Call `mcp__lsp__open_document` to open the file, then call
`mcp__lsp__list_symbols` to understand the containing function and scope:
```
mcp__lsp__open_document({ "file_path": "<file_path>" })
mcp__lsp__list_symbols({ "file_path": "<file_path>" })
```
This establishes:
- Which function contains the selection
- Whether `new_function_name` already exists in the file (name collision check)
**Mandatory name collision check:** If `new_function_name` already exists as a
symbol in the document symbols list, report the conflict and stop immediately:
> Cannot extract: function `new_function_name` already exists in this file.
> Choose a different name and retry.
---
## Step 2 — Check server capabilities
Call `mcp__lsp__get_server_capabilities` to understand what the language server
supports:
```
mcp__lsp__get_server_capabilities({})
```
Check for `codeActionProvider` in the response. Note whether `execute_command`
is listed in `executeCommandProvider.commands`. This determines whether the
primary path (Step 3) is available.
---
## Step 3 — Primary path: LSP code action
Call `mcp__lsp__suggest_fixes` with the selection range:
```
mcp__lsp__suggest_fixes({
"file_path": "<file_path>",
"start_line": N,
"start_column": 1,
"end_line": M,
"end_column": 999
})
```
Filter the returned actions for extract-function actions: include any action
whose `kind` contains `"refactor.extract"` OR whose `title` contains both
"Extract" and "function" (case-insensitive).
**If an extract-function action is found:**
- Display the action title to the user
- If the action proposes a different name than `new_function_name`, ask for
confirmation before proceeding
- Execute via `mcp__lsp__execute_command` if the action has a `command` field:
```
mcp__lsp__execute_command({
"command": "<action.command.command>",
"arguments": <action.command.arguments>
})
```
- OR apply directly via `mcp__lsp__apply_edit` if the action has an `edit` field:
```
mcp__lsp__apply_edit({ "workspace_edit": <action.edit> })
```
- Skip to Step 5 after applying.
**If no extract-function action is found:** fall through to Step 4 (manual fallback).
---
## Step 4 — Manual fallback
When no code action is available, perform manual extraction:
### a) Analyze the selection
Read the selected lines (`start_line` through `end_line`) and identify:
- **Parameters:** Variables used inside the selection that are declared outside
(captured from outer scope — must become function parameters)
- **Return values:** Variables declared inside the selection that are used outside
(must be returned from the extracted function)
- **Early returns:** Return statements inside the selection (the extracted function
must wrap these)
### b) Construct and confirm the proposed signature
Build the extracted function signature based on the captured variables analysis.
Display the proposed signature to the user before writing:
> Proposed extraction:
> ```
> func new_function_name(param1 Type1, param2 Type2) (ReturnType, error) {
> // selected lines
> }
> ```
> Proceed with this signature? [y/n]
Wait for user confirmation before applying any edit.
### c) Apply the extraction (order matters)
Apply edits sequentially — do NOT batch edits from different line regions into a
single `apply_edit` call:
1. **First:** Replace the selected lines with a call to the new function:
```
mcp__lsp__apply_edit({
"workspace_edit": {
"changes": {
"<file_path>": [{
"range": { "start": { "line": start_line-1, "character": 0 },
"end": { "line": end_line, "character": 0 } },
"newText": " result := new_function_name(args...)\n"
}]
}
}
})
```
2. **Second:** Insert the new function definition after the containing function's
closing brace:
```
mcp__lsp__apply_edit({
"workspace_edit": {
"changes": {
"<file_path>": [{
"range": { "start": { "line": insert_line, "character": 0 },
"end": { "line": insert_line, "character": 0 } },
"newText": "\nfunc new_function_name(params) ReturnType {\n ...\n}\n"
}]
}
}
})
```
Apply call-site replacement first, then insert the new function. This order
preserves line numbers during editing: replacing call site does not shift the
insertion point for the new function definition.
---
## Step 5 — Validate
After extraction via either path:
### 1. Check diagnostics
```
mcp__lsp__get_diagnostics({ "file_path": "<file_path>" })
```
If errors are reported, display them with the table of common causes below.
### 2. Common post-extraction errors
| Error type | Likely cause | Fix |
|------------|--------------|-----|
| Undefined variable | Captured var not passed as parameter | Add parameter |
| Type mismatch | Return type inferred incorrectly | Adjust return type in signature |
| Name shadows outer | New function name matches outer scope | Choose different name |
| Unused variable | Return value not captured at call site | Add variable at call site |
### 3. Format the document
```
mcp__lsp__format_document({ "file_path": "<file_path>" })
```
This cleans up indentation introduced by the extraction.
---
## Output Format
After completing extraction, display:
```
## Extraction Summary
- File: path/to/file.go
- Extracted: lines N–M
- New function: new_function_name
- Path used: LSP code action / Manual fallback
- Post-extraction errors: 0
```
Follow with the Diagnostic Summary if any errors changed (format in
[references/patterns.md](references/patterns.md)).
---
## Language-Specific Notes
- **Go:** gopls may offer "Extract function" in code actions for selection ranges.
Check code actions first; gopls support varies by version.
- **TypeScript/JavaScript:** typescript-language-server may offer "Extract to function in global scope"
or "Extract to inner function" — filter for these titles in Step 3.
- **Python:** pylsp and pyright-langserver typically do NOT offer extract-function
code actions. Manual fallback (Step 4) is required for Python files.
© blackwell-systems · MIT · blackwell-systems/agent-lsp