Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project follows Semantic Versioning.

## [1.1.1]

### Added
- Added functions to extract and replace module specifiers in import statements.
- Added `@Tool` decorator to store method metadata without exposing proxy methods as tools.

### Changed
- Implemented relative import rewriting for generated proxy files.
- Enhanced proxy generation to include tool metadata in generated methods.
- Updated proxy scaffold template to conditionally emit `@Tool` based on available method metadata.
- Extended runtime LM tool registration to support both `@ExposeTool` and `@Tool` decorated methods.

### Fixed
- Ensured grouped imports retain only symbols actually used by generated proxy method signatures.

## [1.1.0] - 2026-04-29

### Added
Expand All @@ -25,5 +40,6 @@ The format is based on Keep a Changelog, and this project follows Semantic Versi
### Added
- Initial release baseline.

[1.1.1]: https://github.com/codearchitects/mcp-scanner/releases/tag/v1.1.1
[1.1.0]: https://github.com/codearchitects/mcp-scanner/releases/tag/v1.1.0
[1.0.3]: https://github.com/codearchitects/mcp-scanner/releases/tag/v1.0.3
72 changes: 71 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,24 @@ Then declare it in your extension's `package.json`:
| `icon` | `string` | | VS Code codicon, e.g. `$(search)`. Default: `$(tools)` |
| `canBeReferencedInPrompt` | `boolean` | | Allow `#tool` references. Default: `true` |

### `Tool(options)` — Method Decorator

Used by code generators (e.g., proxy generation) to preserve tool metadata without exposing the method.
Accepts the same options as `@ExposeTool` but does not register the method as a tool.

| Option | Type | Required | Description |
|--------|------|----------|-------------|
| `name` | `string` | ✅ | Unique tool name |
| `displayName` | `string` | ✅ | Human-readable label |
| `modelDescription` | `string` | ✅ | Description for the language model |
| `icon` | `string` | | VS Code codicon, e.g. `$(search)`. Default: `$(tools)` |
| `canBeReferencedInPrompt` | `boolean` | | Allow `#tool` references. Default: `true` |

### `registerExposedTools(context, instances)` — Runtime Registration

Registers methods decorated with either `@ExposeTool` or `@Tool` as runtime LM tool handlers.
This is useful for proxy-based architectures where generated proxy methods carry `@Tool` metadata.

### `scanProject(projectRoot, tsconfigFileName?)` — Scanner

Returns `IScanResult` with discovered tools, file count, and diagnostics.
Expand Down Expand Up @@ -254,6 +272,7 @@ The scaffold template (EJS) has access to:
- `className`: The class name (set via `--proxy-class` or from template)
- `methods`: Array of method objects with:
- `toolName`: Tool name from `@ExposeTool({ name })`
- `toolOptions`: Complete metadata object from `@ExposeTool({ displayName, modelDescription, icon, canBeReferencedInPrompt })`
- `methodName`: Source method name
- `returnTypeText`: Return type text
- `jsDoc`: JSDoc comment from source
Expand All @@ -268,14 +287,17 @@ The scaffold template (EJS) has access to:
/* This file is auto-generated by mcp-scanner. */

// <mcp-scanner:proxy-imports:start>
import { Tool } from 'mcp-scanner';
<%- methods.flatMap(m => m.importStatements).filter((v, i, a) => a.indexOf(v) === i).sort().join('\n') %>
// <mcp-scanner:proxy-imports:end>

// <mcp-scanner:proxy-class:start>
export class <%- className %> {
// <mcp-scanner:proxy-class:end>
// <mcp-scanner:proxy-methods:start>
// methods generated here
// JSDoc comment
// @Tool decorator with metadata
// public async method(...) { ... }
// <mcp-scanner:proxy-methods:end>
}
```
Expand Down Expand Up @@ -308,6 +330,54 @@ Injection markers used by the generator:
// <mcp-scanner:proxy-methods:end>
```

## Preserving Tool Metadata in Proxies

When generating proxy methods, `mcp-scanner` automatically applies the `@Tool` decorator to preserve all original tool metadata (displayName, modelDescription, icon, canBeReferencedInPrompt).

### Why `@Tool` and not `@ExposeTool`?

- **`@ExposeTool`** exposes the method as a tool, which would cause the _proxy_ method to appear in `languageModelTools` in addition to the original. This is not desired.
- **`@Tool`** preserves metadata without exposure, allowing language model tools and MCP servers to access the complete tool definition without the proxy being registered as a separate tool.

### Example Generated Proxy

Given a source library method:

```typescript
class MyLibrary {
@ExposeTool({
name: 'processData',
displayName: 'Process Data',
modelDescription: 'Transforms data according to rules.',
icon: '$(gear)',
})
processData(params: IDataParams): Promise<string> { ... }
}
```

The generated proxy preserves the metadata:

```typescript
class MyProxyClass {
/**
* Transforms data according to rules.
*/
@Tool({
name: 'processData',
displayName: 'Process Data',
modelDescription: 'Transforms data according to rules.',
icon: '$(gear)',
})
public async processData(params: IDataParams): Promise<string> {
// TODO: Replace with your implementation
return await this._bridge.dispatch('processData', params);
}
}
```

The `@Tool` decorator is automatically imported from `mcp-scanner` in the proxy file's imports section.


## How It Works

1. Loads the project's `tsconfig.json` and creates a TypeScript program
Expand Down
7 changes: 4 additions & 3 deletions coverage/coverage-summary.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{"total": {"lines":{"total":513,"covered":439,"skipped":0,"pct":85.57},"statements":{"total":523,"covered":448,"skipped":0,"pct":85.65},"functions":{"total":70,"covered":63,"skipped":0,"pct":90},"branches":{"total":410,"covered":298,"skipped":0,"pct":72.68},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}}
{"total": {"lines":{"total":568,"covered":480,"skipped":0,"pct":84.5},"statements":{"total":582,"covered":492,"skipped":0,"pct":84.53},"functions":{"total":85,"covered":76,"skipped":0,"pct":89.41},"branches":{"total":455,"covered":329,"skipped":0,"pct":72.3},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}}
,"C:\\Projects\\CodeArchitects\\mcp-scanner\\src\\patcher.ts": {"lines":{"total":76,"covered":65,"skipped":0,"pct":85.52},"functions":{"total":13,"covered":12,"skipped":0,"pct":92.3},"statements":{"total":82,"covered":70,"skipped":0,"pct":85.36},"branches":{"total":61,"covered":44,"skipped":0,"pct":72.13}}
,"C:\\Projects\\CodeArchitects\\mcp-scanner\\src\\proxy-generator.ts": {"lines":{"total":117,"covered":96,"skipped":0,"pct":82.05},"functions":{"total":20,"covered":16,"skipped":0,"pct":80},"statements":{"total":117,"covered":96,"skipped":0,"pct":82.05},"branches":{"total":84,"covered":59,"skipped":0,"pct":70.23}}
,"C:\\Projects\\CodeArchitects\\mcp-scanner\\src\\proxy-scanner.ts": {"lines":{"total":150,"covered":123,"skipped":0,"pct":82},"functions":{"total":18,"covered":16,"skipped":0,"pct":88.88},"statements":{"total":152,"covered":125,"skipped":0,"pct":82.23},"branches":{"total":113,"covered":72,"skipped":0,"pct":63.71}}
,"C:\\Projects\\CodeArchitects\\mcp-scanner\\src\\proxy-generator.ts": {"lines":{"total":128,"covered":106,"skipped":0,"pct":82.81},"functions":{"total":25,"covered":21,"skipped":0,"pct":84},"statements":{"total":129,"covered":107,"skipped":0,"pct":82.94},"branches":{"total":88,"covered":64,"skipped":0,"pct":72.72}}
,"C:\\Projects\\CodeArchitects\\mcp-scanner\\src\\proxy-scanner.ts": {"lines":{"total":188,"covered":148,"skipped":0,"pct":78.72},"functions":{"total":25,"covered":21,"skipped":0,"pct":84},"statements":{"total":193,"covered":152,"skipped":0,"pct":78.75},"branches":{"total":150,"covered":94,"skipped":0,"pct":62.66}}
,"C:\\Projects\\CodeArchitects\\mcp-scanner\\src\\decorators\\expose-tool.decorator.ts": {"lines":{"total":6,"covered":6,"skipped":0,"pct":100},"functions":{"total":3,"covered":3,"skipped":0,"pct":100},"statements":{"total":6,"covered":6,"skipped":0,"pct":100},"branches":{"total":4,"covered":4,"skipped":0,"pct":100}}
,"C:\\Projects\\CodeArchitects\\mcp-scanner\\src\\decorators\\tool.decorator.ts": {"lines":{"total":6,"covered":6,"skipped":0,"pct":100},"functions":{"total":3,"covered":3,"skipped":0,"pct":100},"statements":{"total":6,"covered":6,"skipped":0,"pct":100},"branches":{"total":4,"covered":4,"skipped":0,"pct":100}}
,"C:\\Projects\\CodeArchitects\\mcp-scanner\\src\\scanner\\tool-scanner.ts": {"lines":{"total":164,"covered":149,"skipped":0,"pct":90.85},"functions":{"total":16,"covered":16,"skipped":0,"pct":100},"statements":{"total":166,"covered":151,"skipped":0,"pct":90.96},"branches":{"total":148,"covered":119,"skipped":0,"pct":80.4}}
}
46 changes: 23 additions & 23 deletions coverage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,30 @@ <h1>All files</h1>
<div class='clearfix'>

<div class='fl pad1y space-right2'>
<span class="strong">85.65% </span>
<span class="strong">84.53% </span>
<span class="quiet">Statements</span>
<span class='fraction'>448/523</span>
<span class='fraction'>492/582</span>
</div>


<div class='fl pad1y space-right2'>
<span class="strong">72.68% </span>
<span class="strong">72.3% </span>
<span class="quiet">Branches</span>
<span class='fraction'>298/410</span>
<span class='fraction'>329/455</span>
</div>


<div class='fl pad1y space-right2'>
<span class="strong">90% </span>
<span class="strong">89.41% </span>
<span class="quiet">Functions</span>
<span class='fraction'>63/70</span>
<span class='fraction'>76/85</span>
</div>


<div class='fl pad1y space-right2'>
<span class="strong">85.57% </span>
<span class="strong">84.5% </span>
<span class="quiet">Lines</span>
<span class='fraction'>439/513</span>
<span class='fraction'>480/568</span>
</div>


Expand Down Expand Up @@ -80,17 +80,17 @@ <h1>All files</h1>
</thead>
<tbody><tr>
<td class="file high" data-value="src"><a href="src/index.html">src</a></td>
<td data-value="82.9" class="pic high">
<div class="chart"><div class="cover-fill" style="width: 82%"></div><div class="cover-empty" style="width: 18%"></div></div>
<td data-value="81.43" class="pic high">
<div class="chart"><div class="cover-fill" style="width: 81%"></div><div class="cover-empty" style="width: 19%"></div></div>
</td>
<td data-value="82.9" class="pct high">82.9%</td>
<td data-value="351" class="abs high">291/351</td>
<td data-value="67.82" class="pct medium">67.82%</td>
<td data-value="258" class="abs medium">175/258</td>
<td data-value="86.27" class="pct high">86.27%</td>
<td data-value="51" class="abs high">44/51</td>
<td data-value="82.79" class="pct high">82.79%</td>
<td data-value="343" class="abs high">284/343</td>
<td data-value="81.43" class="pct high">81.43%</td>
<td data-value="404" class="abs high">329/404</td>
<td data-value="67.55" class="pct medium">67.55%</td>
<td data-value="299" class="abs medium">202/299</td>
<td data-value="85.71" class="pct high">85.71%</td>
<td data-value="63" class="abs high">54/63</td>
<td data-value="81.37" class="pct high">81.37%</td>
<td data-value="392" class="abs high">319/392</td>
</tr>

<tr>
Expand All @@ -99,13 +99,13 @@ <h1>All files</h1>
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="6" class="abs high">6/6</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="4" class="abs high">4/4</td>
<td data-value="12" class="abs high">12/12</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="3" class="abs high">3/3</td>
<td data-value="8" class="abs high">8/8</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="6" class="abs high">6/6</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="12" class="abs high">12/12</td>
</tr>

<tr>
Expand All @@ -131,7 +131,7 @@ <h1>All files</h1>
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-04-29T09:35:03.148Z
at 2026-04-29T14:41:53.380Z
</div>
<script src="prettify.js"></script>
<script>
Expand Down
2 changes: 1 addition & 1 deletion coverage/src/decorators/expose-tool.decorator.ts.html
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ <h1><a href="../../index.html">All files</a> / <a href="index.html">src/decorato
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-04-29T09:35:03.148Z
at 2026-04-29T14:41:53.380Z
</div>
<script src="../../prettify.js"></script>
<script>
Expand Down
25 changes: 20 additions & 5 deletions coverage/src/decorators/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,28 @@ <h1><a href="../../index.html">All files</a> src/decorators</h1>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Statements</span>
<span class='fraction'>6/6</span>
<span class='fraction'>12/12</span>
</div>


<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Branches</span>
<span class='fraction'>4/4</span>
<span class='fraction'>8/8</span>
</div>


<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>3/3</span>
<span class='fraction'>6/6</span>
</div>


<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>6/6</span>
<span class='fraction'>12/12</span>
</div>


Expand Down Expand Up @@ -93,6 +93,21 @@ <h1><a href="../../index.html">All files</a> src/decorators</h1>
<td data-value="6" class="abs high">6/6</td>
</tr>

<tr>
<td class="file high" data-value="tool.decorator.ts"><a href="tool.decorator.ts.html">tool.decorator.ts</a></td>
<td data-value="100" class="pic high">
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="6" class="abs high">6/6</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="4" class="abs high">4/4</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="3" class="abs high">3/3</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="6" class="abs high">6/6</td>
</tr>

</tbody>
</table>
</div>
Expand All @@ -101,7 +116,7 @@ <h1><a href="../../index.html">All files</a> src/decorators</h1>
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-04-29T09:35:03.148Z
at 2026-04-29T14:41:53.380Z
</div>
<script src="../../prettify.js"></script>
<script>
Expand Down
Loading
Loading