Skip to content

Commit 6487410

Browse files
feat(chore): access-control interceptor support for MCP endpoint
access-control interceptor support for MCP endpoint GH-1
1 parent 9f21f07 commit 6487410

File tree

16 files changed

+545
-410
lines changed

16 files changed

+545
-410
lines changed

.github/workflows/main.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
with:
1919
node-version: ${{ matrix.node-version }}
2020
- name: Install Dependencies
21-
run: npm ci
21+
run: npm ci --ignore-scripts
2222
- name: Run Test Cases
2323
run: npm run test
2424

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
CONFIG_EMAIL: ${{ vars.RELEASE_COMMIT_EMAIL }}
4040

4141
- name: Install 📌
42-
run: npm install
42+
run: npm install --ignore-scripts
4343

4444
- name: Test 🔧
4545
run: npm run test

README.md

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ The schema field allows defining a Zod-based validation schema for tool input pa
5454
To use hooks with MCP tools, follow the provider-based approach:
5555

5656
Step 1: Create a hook provider:
57-
```typescript
57+
```ts
5858
// src/providers/my-hook.provider.ts
5959
export class MyHookProvider implements Provider<McpHookFunction> {
6060
constructor(@inject(LOGGER.LOGGER_INJECT) private logger: ILogger) {}
@@ -66,7 +66,7 @@ The schema field allows defining a Zod-based validation schema for tool input pa
6666
}
6767
```
6868
Step 2: Add binding key to McpHookBindings:
69-
```typescript
69+
```ts
7070
// src/keys.ts
7171
export namespace McpHookBindings {
7272
export const MY_HOOK = BindingKey.create<McpHookFunction>('hooks.mcp.myHook');
@@ -77,10 +77,64 @@ The schema field allows defining a Zod-based validation schema for tool input pa
7777
this.bind(McpHookBindings.MY_HOOK).toProvider(MyHookProvider);
7878
```
7979
Step 4: Use in decorator:
80-
```typescript
80+
```ts
8181
@mcpTool({
8282
name: 'my-tool',
8383
description: 'my-description'
8484
preHookBinding: McpHookBindings.MY_HOOK,
8585
postHookBinding: 'hooks.mcp.myOtherHook' // or string binding key
8686
})
87+
```
88+
## MCP Access Control
89+
By default, any authenticated user can call the `/mcp` endpoint.
90+
To restrict which users can access MCP functionality, this extension supports adding a custom LoopBack 4 interceptor.
91+
92+
Create an interceptor that validates the authenticated user before the request reaches the MCP controller.
93+
94+
```ts
95+
import {
96+
InvocationContext,
97+
InvocationResult,
98+
Provider,
99+
ValueOrPromise,
100+
inject,
101+
interceptor,
102+
} from '@loopback/core';
103+
import {HttpErrors} from '@loopback/rest';
104+
import {AuthenticationBindings, IAuthUser} from 'loopback4-authentication';
105+
106+
export class McpAccessInterceptor implements Provider<Interceptor> {
107+
constructor(
108+
@inject(AuthenticationBindings.CURRENT_USER, {optional: true})
109+
private readonly currentUser: IAuthUser,
110+
) {}
111+
112+
value() {
113+
return this.intercept.bind(this);
114+
}
115+
116+
intercept(
117+
invocationCtx: InvocationContext,
118+
next: () => ValueOrPromise<InvocationResult>,
119+
): ValueOrPromise<InvocationResult> {
120+
if (!this.currentUser) {
121+
throw new HttpErrors.Unauthorized('User not authenticated');
122+
}
123+
124+
const user = this.currentUser;
125+
126+
// Example rule: only admins or users with `access-mcp` permission
127+
if (user.role !== 'admin' && !user.permissions?.includes('access-mcp')) {
128+
throw new HttpErrors.Forbidden(
129+
`User ${user.username} is not allowed to access MCP`,
130+
);
131+
}
132+
133+
return next();
134+
}
135+
}
136+
```
137+
Bind it in your `application.ts`:
138+
```ts
139+
this.bind(McpBindings.ACCESS_INTERCEPTOR).toProvider(McpAccessInterceptor);
140+
```

0 commit comments

Comments
 (0)