Skip to content

Commit 76fb57a

Browse files
committed
feat(mcp): add MCP extension for exposing APIs as MCP tools
Add @loopback/mcp extension that enables LoopBack applications to expose their API endpoints as Model Context Protocol (MCP) tools. Includes decorator-based tool registration, schema generation, and server factory.
1 parent 7ea0d92 commit 76fb57a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+31439
-21186
lines changed

CODEOWNERS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,14 @@
335335
# - Standby owner(s): @samarpanB
336336
/extensions/sequelize @shubhamp-sf @samarpanB
337337

338+
#
339+
# MCP
340+
#
341+
# - Issue label: mcp
342+
# - Primary owner(s): @akshatdubeysf
343+
# - Standby owner(s): @samarpanB
344+
/extensions/mcp @akshatdubeysf @samarpanB
345+
338346
#
339347
# MessagePack body parser
340348
#

docs/site/Component.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ These components add additional capabilities to LoopBack.
199199
Adds support for TypeORM in LoopBack
200200
- [@loopback/sequelize](https://github.com/loopbackio/loopback-next/tree/master/extensions/sequelize) -
201201
Adds support for Sequelize in LoopBack
202+
- [@loopback/mcp](https://github.com/loopbackio/loopback-next/tree/master/extensions/mcp) -
203+
Adds support to expose any controller as an MCP server
202204

203205
### Community extensions
204206

docs/site/MCP.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
lang: en
3+
title: 'Model Context Protocol (MCP)'
4+
keywords: LoopBack 4.0, LoopBack 4, Node.js, TypeScript, OpenAPI, MCP
5+
layout: readme
6+
source: loopback-next
7+
file: extensions/mcp/README.md
8+
sidebar: lb4_sidebar
9+
permalink: /doc/en/lb4/MCP.html
10+
summary: Use `@loopback/mcp` to expose LoopBack APIs as MCP tools
11+
---

docs/site/MONOREPO.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ one in the monorepo: `npm run update-monorepo-file`
5454
| [extensions/pooling](https://github.com/loopbackio/loopback-next/tree/master/extensions/pooling) | @loopback/pooling | Resource pooling service for LoopBack 4 |
5555
| [extensions/socketio](https://github.com/loopbackio/loopback-next/tree/master/extensions/socketio) | @loopback/socketio | LoopBack's WebSocket server based on socket.io |
5656
| [extensions/typeorm](https://github.com/loopbackio/loopback-next/tree/master/extensions/typeorm) | @loopback/typeorm | Adds support for TypeORM in LoopBack |
57-
| [extensions/sequelize](https://github.com/loopbackio/loopback-next/tree/master/extensions/sequelize) | @loopback/sequelize | An extension that adds support for Sequelize to existing repositories. |
57+
| [extensions/sequelize](https://github.com/loopbackio/loopback-next/tree/master/extensions/sequelize) | @loopback/sequelize | An extension that adds support for Sequelize to existing repositories.
58+
59+
| [extensions/mcp](https://github.com/loopbackio/loopback-next/tree/master/extensions/mcp) | @loopback/mcp | An extension that adds support to expose a controller as an MCP server |
5860
| [fixtures/mock-oauth2-provider](https://github.com/loopbackio/loopback-next/tree/master/fixtures/mock-oauth2-provider) | @loopback/mock-oauth2-provider | An internal application to mock the OAuth2 authorization flow login with a social app like facebook, google etc |
5961
| [fixtures/tsdocs-monorepo](https://github.com/loopbackio/loopback-next/tree/master/fixtures/tsdocs-monorepo) | _(private)_ | A monorepo for tsdocs testing |
6062
| [packages/authentication](https://github.com/loopbackio/loopback-next/tree/master/packages/authentication) | @loopback/authentication | A LoopBack component for authentication support. |

docs/site/sidebars/lb4_sidebar.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,10 @@ children:
521521
url: Pooling.html
522522
output: 'web, pdf'
523523

524+
- title: 'Model Context Protocol (MCP)'
525+
url: MCP.html
526+
output: 'web, pdf'
527+
524528
- title: 'Troubleshooting'
525529
url: Troubleshooting.html
526530
output: 'web, pdf'

extensions/mcp/.npmrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
package-lock=true
2+
scripts-prepend-node-path=true

extensions/mcp/LICENSE

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Copyright (c) LoopBack contributors 2026.
2+
Node module: @loopback/mcp
3+
This project is licensed under the MIT License, full text below.
4+
5+
---
6+
7+
MIT license
8+
9+
Permission is hereby granted, free of charge, to any person obtaining a copy
10+
of this software and associated documentation files (the "Software"), to deal
11+
in the Software without restriction, including without limitation the rights
12+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
copies of the Software, and to permit persons to whom the Software is
14+
furnished to do so, subject to the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included in
17+
all copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.

extensions/mcp/README.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<a href="https://sourcefuse.github.io/arc-docs/arc-api-docs" target="_blank"><img src="https://github.com/sourcefuse/loopback4-microservice-catalog/blob/master/docs/assets/logo-dark-bg.png?raw=true" alt="ARC By SourceFuse logo" title="ARC By SourceFuse" align="right" width="150" /></a>
2+
3+
# [loopback4-mcp](https://github.com/sourcefuse/loopback4-mcp)
4+
5+
<p align="left">
6+
<a href="https://www.npmjs.com/package/loopback4-mcp">
7+
<img src="https://img.shields.io/npm/v/loopback4-mcp.svg" alt="npm version" />
8+
</a>
9+
<a href="https://sonarcloud.io/summary/new_code?id=sourcefuse_loopback4-mcp" target="_blank">
10+
<img alt="Sonar Quality Gate" src="https://img.shields.io/sonar/quality_gate/sourcefuse_loopback4-mcp?server=https%3A%2F%2Fsonarcloud.io">
11+
</a>
12+
<a href="https://github.com/sourcefuse/loopback4-mcp/graphs/contributors" target="_blank">
13+
<img alt="GitHub contributors" src="https://img.shields.io/github/contributors/sourcefuse/loopback4-mcp?">
14+
</a>
15+
<a href="https://www.npmjs.com/package/loopback4-mcp" target="_blank">
16+
<img alt="downloads" src="https://img.shields.io/npm/dw/loopback4-mcp.svg">
17+
</a>
18+
<a href="./LICENSE">
19+
<img src="https://img.shields.io/github/license/sourcefuse/loopback4-mcp.svg" alt="License" />
20+
</a>
21+
<a href="https://loopback.io/" target="_blank">
22+
<img alt="Powered By LoopBack 4" src="https://img.shields.io/badge/Powered%20by-LoopBack 4-brightgreen" />
23+
</a>
24+
</p>
25+
26+
## Overview
27+
28+
This extension provides a plug-and-play integration between LoopBack4 applications and the Model Context Protocol (MCP) specification.
29+
30+
Its purpose is to enable LoopBack APIs, services, and business logic to be exposed as MCP Tools, allowing external MCP clients (such as LLMs, agents, or MCP-compatible apps) to discover and execute server-defined operations.
31+
32+
### Key Features
33+
34+
- Automatic MCP Tool Discovery :-
35+
The extension scans your application at boot time and automatically registers all methods decorated with the custom @mcpTool() decorator.
36+
37+
This allows you to define MCP tools anywhere in your LoopBack project without manually wiring metadata.
38+
39+
- Lifecycle-managed Tool Registry :-
40+
A dedicated `McpToolRegistry` service maintains all discovered tool metadata,their handlers and execution context.
41+
42+
A `McpToolRegistryBootObserver` ensures that registration happens only after the application has fully booted.
43+
44+
## Installation
45+
46+
```sh
47+
npm install @loopback/mcp
48+
```
49+
50+
## Basic Usage
51+
52+
Configure and load `McpComponent` in the application constructor
53+
as shown below.
54+
55+
```ts
56+
import {McpComponent} from 'loopback4-mcp';
57+
58+
export class MyApplication extends BootMixin(
59+
ServiceMixin(RepositoryMixin(RestApplication)),
60+
) {
61+
constructor(options: ApplicationConfig = {}) {
62+
super();
63+
this.component(McpComponent);
64+
}
65+
}
66+
```
67+
68+
Add the `@mcpTool()` decorator to any controller in your application.
69+
70+
All MCP tool methods must use LoopBack `@param` decorators to define their input parameters.If `@param` decorators are missing, the MCP tool will fail.
71+
72+
```ts
73+
@mcpTool({
74+
name: 'create-user',
75+
description: 'Creates a new user in the system',
76+
schema?: {
77+
email: z.string().email(),
78+
name: z.string(),
79+
},
80+
})
81+
async createUser(
82+
@param.query.string('email') email: string,
83+
@param.query.string('name') name: string,
84+
) {
85+
return {message: `User ${name} created`};
86+
}
87+
```
88+
89+
This decorator accepts a total of five fields, out of which `name` and `description` are mandatory and `schema`,`preHook` and `postHook` are optional enhancements.
90+
91+
The schema field allows defining a Zod-based validation schema for tool input parameters, while preHook and postHook enable execution of custom logic before and after the tool handler runs.
92+
93+
## Mcp Hook Usage Details
94+
95+
To use hooks with MCP tools, follow the provider-based approach:
96+
97+
Step 1: Create a hook provider:
98+
99+
```ts
100+
// src/providers/my-hook.provider.ts
101+
export class MyHookProvider implements Provider<McpHookFunction> {
102+
constructor(@inject(LOGGER.LOGGER_INJECT) private logger: ILogger) {}
103+
value(): McpHookFunction {
104+
return async (context: McpHookContext) => {
105+
this.logger.info(`Hook executed for tool: ${context.toolName}`);
106+
};
107+
}
108+
}
109+
```
110+
111+
Step 2: Add binding key to McpHookBindings:
112+
113+
```ts
114+
// src/keys.ts
115+
export namespace McpHookBindings {
116+
export const MY_HOOK = BindingKey.create<McpHookFunction>('hooks.mcp.myHook');
117+
}
118+
```
119+
120+
Step 3: Bind provider in `application.ts`:
121+
122+
```typescript
123+
this.bind(McpHookBindings.MY_HOOK).toProvider(MyHookProvider);
124+
```
125+
126+
Step 4: Use in decorator:
127+
128+
```ts
129+
@mcpTool({
130+
name: 'my-tool',
131+
description: 'my-description'
132+
preHookBinding: McpHookBindings.MY_HOOK,
133+
postHookBinding: 'hooks.mcp.myOtherHook' // or string binding key
134+
})
135+
```

extensions/mcp/package.json

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
{
2+
"name": "@loopback/mcp",
3+
"version": "0.0.1",
4+
"private": false,
5+
"description": "A loopback extension that exposes API to MCP Tools.",
6+
"keywords": [
7+
"loopback-extension",
8+
"loopback",
9+
"loopback4",
10+
"mcp",
11+
"mcp-tool"
12+
],
13+
"main": "dist/index.js",
14+
"types": "dist/index.d.ts",
15+
"engines": {
16+
"node": ">=20"
17+
},
18+
"scripts": {
19+
"build": "lb-tsc",
20+
"build:watch": "lb-tsc --watch",
21+
"lint": "npm run eslint && npm run prettier:check",
22+
"lint:fix": "npm run eslint:fix && npm run prettier:fix",
23+
"prettier:cli": "lb-prettier \"**/*.ts\" \"**/*.js\"",
24+
"prettier:check": "npm run prettier:cli -- -l",
25+
"prettier:fix": "npm run prettier:cli -- --write",
26+
"eslint": "lb-eslint --report-unused-disable-directives .",
27+
"eslint:fix": "npm run eslint -- --fix",
28+
"pretest": "npm run rebuild",
29+
"test": "lb-mocha --allow-console-logs \"dist/__tests__\"",
30+
"posttest": "npm run lint",
31+
"test:dev": "lb-mocha --allow-console-logs dist/__tests__/**/*.js && npm run posttest",
32+
"clean": "lb-clean dist *.tsbuildinfo .eslintcache",
33+
"rebuild": "npm run clean && npm run build",
34+
"prepublishOnly": "npm run test"
35+
},
36+
"repository": {
37+
"type": "git",
38+
"url": "https://github.com/sourcefuse/loopback4-mcp"
39+
},
40+
"author": "SourceFuse",
41+
"license": "MIT",
42+
"files": [
43+
"README.md",
44+
"dist",
45+
"src",
46+
"!*/__tests__"
47+
],
48+
"peerDependencies": {
49+
"@loopback/core": "^7.0.3"
50+
},
51+
"dependencies": {
52+
"@loopback/rest": "^15.0.7",
53+
"@modelcontextprotocol/sdk": "^1.24.0",
54+
"@sourceloop/core": "^20.0.0",
55+
"tslib": "^2.6.2",
56+
"zod": "^4.2.1"
57+
},
58+
"devDependencies": {
59+
"@commitlint/cli": "^17.7.1",
60+
"@commitlint/config-conventional": "^17.7.0",
61+
"@loopback/boot": "^8.0.11",
62+
"@loopback/build": "^12.0.3",
63+
"@loopback/core": "^7.0.3",
64+
"@loopback/eslint-config": "^16.0.1",
65+
"@loopback/repository": "^8.0.10",
66+
"@loopback/rest-explorer": "^8.0.11",
67+
"@loopback/service-proxy": "^8.0.10",
68+
"@loopback/testlab": "^8.0.3",
69+
"@semantic-release/changelog": "^6.0.1",
70+
"@semantic-release/commit-analyzer": "^9.0.2",
71+
"@semantic-release/git": "^10.0.1",
72+
"@semantic-release/npm": "^13.1.3",
73+
"@semantic-release/release-notes-generator": "^10.0.3",
74+
"@types/jsonwebtoken": "^9.0.10",
75+
"@types/node": "^16.18.126",
76+
"commitizen": "^4.2.4",
77+
"cz-conventional-changelog": "^3.3.0",
78+
"cz-customizable": "^6.3.0",
79+
"cz-customizable-ghooks": "^2.0.0",
80+
"eslint": "^8.57.1",
81+
"husky": "^7.0.4",
82+
"loopback4-authentication": "^13.0.4",
83+
"loopback4-authorization": "^8.1.3",
84+
"semantic-release": "^25.0.2",
85+
"source-map-support": "^0.5.21",
86+
"typescript": "~5.2.2"
87+
},
88+
"overrides": {
89+
"jws": "3.2.3",
90+
"node-forge": "1.3.2",
91+
"qs": "6.14.1"
92+
},
93+
"config": {
94+
"commitizen": {
95+
"path": "./node_modules/cz-customizable"
96+
},
97+
"cz-customizable": {
98+
"config": "./.cz-config.js"
99+
}
100+
},
101+
"publishConfig": {
102+
"registry": "https://registry.npmjs.org/",
103+
"access": "public"
104+
},
105+
"release": {
106+
"branches": [
107+
"master"
108+
],
109+
"plugins": [
110+
[
111+
"@semantic-release/commit-analyzer",
112+
{
113+
"preset": "angular",
114+
"releaseRules": [
115+
{
116+
"type": "chore",
117+
"scope": "deps",
118+
"release": "patch"
119+
}
120+
]
121+
}
122+
],
123+
"@semantic-release/release-notes-generator",
124+
[
125+
"@semantic-release/npm",
126+
{
127+
"npmPublish": true,
128+
"pkgRoot": ".",
129+
"tarballDir": "dist"
130+
}
131+
],
132+
[
133+
"@semantic-release/git",
134+
{
135+
"assets": [
136+
"package.json",
137+
"CHANGELOG.md"
138+
],
139+
"message": "chore(release): ${nextRelease.version} semantic"
140+
}
141+
],
142+
"@semantic-release/github"
143+
],
144+
"repositoryUrl": "https://github.com/sourcefuse/loopback4-mcp.git"
145+
}
146+
}

0 commit comments

Comments
 (0)