Skip to content

Commit 7c9794c

Browse files
feat(lightspeed): add DCR authentication support for MCP servers
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 763507e commit 7c9794c

21 files changed

Lines changed: 2613 additions & 1056 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-lightspeed-backend': minor
3+
'@red-hat-developer-hub/backstage-plugin-lightspeed': minor
4+
---
5+
6+
Add DCR (Dynamic Client Registration) authentication support for Backstage-internal MCP servers, allowing the Lightspeed backend to mint per-user plugin request tokens instead of requiring static tokens.

workspaces/lightspeed/.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
dist
2+
dist-dynamic
23
dist-types
34
coverage
45
.vscode

workspaces/lightspeed/app-config.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,27 @@ organization:
1818

1919
# Disable AI Notebooks feature by default
2020
lightspeed:
21+
# MCP Servers available to the Lightspeed assistant via LCORE.
22+
# Names must match the 'name' field in lightspeed-stack.yaml mcp_servers.
23+
#
24+
# Authentication modes per server:
25+
# auth: dcr — Backstage mints a per-user token automatically (for
26+
# Backstage-internal MCP servers with DCR enabled).
27+
# The corresponding lightspeed-stack.yaml entry should use
28+
# authorization_headers: { Authorization: "client" }.
29+
# Any static 'token' field is ignored when auth: dcr is set.
30+
#
31+
# (no auth) — Static-token mode (legacy). Uses the 'token' field from
32+
# this config, or the user's personal token set in the UI.
33+
# The corresponding lightspeed-stack.yaml entry should use
34+
# authorization_headers: { Authorization: "client" }.
35+
#
36+
# Example:
37+
# mcpServers:
38+
# - name: mcp-integration-tools
39+
# auth: dcr
40+
# - name: test-mcp-server
41+
# token: ${MCP_TOKEN_1}
2142
notebooks:
2243
enabled: ${NOTEBOOKS_ENABLED:-false}
2344
queryDefaults:
@@ -31,6 +52,16 @@ backend:
3152
# auth:
3253
# keys:
3354
# - secret: ${BACKEND_SECRET}
55+
#
56+
# To expose MCP tools locally, uncomment the following. pluginSources lists
57+
# the pluginIds whose actions are exposed as MCP tools. Requires
58+
# @backstage/plugin-mcp-actions-backend and the mcp-extras plugins in index.ts.
59+
# In production RHDH the overlay renames these to 'software-catalog-mcp-tool' etc.
60+
# actions:
61+
# pluginSources:
62+
# - 'software-catalog-mcp-extras'
63+
# - 'techdocs-mcp-extras'
64+
# - 'scaffolder-mcp-extras'
3465
baseUrl: http://localhost:7007
3566
listen:
3667
port: 7007
@@ -63,6 +94,12 @@ backend:
6394
# password: ${POSTGRES_PASSWORD}
6495
# workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir
6596

97+
# Disable dotted tool names (e.g. "software-catalog-mcp-extras.query-catalog-entities")
98+
# so they pass OpenAI's strict tool name pattern ^[a-zA-Z0-9_-]+$ (dots are rejected).
99+
# In production RHDH, add this under the backstage appConfig in values.yaml.
100+
# mcpActions:
101+
# namespacedToolNames: false
102+
66103
integrations:
67104
github:
68105
- host: github.com
@@ -95,6 +132,14 @@ techdocs:
95132
auth:
96133
# see https://backstage.io/docs/auth/ to learn about auth providers
97134
environment: development
135+
# Enable Dynamic Client Registration (DCR) for MCP.
136+
# This allows MCP clients (e.g. Cursor, VS Code) to register OAuth2 clients
137+
# dynamically and obtain per-user tokens via the Backstage auth system.
138+
# Requires @backstage/plugin-auth (frontend) and @backstage/plugin-mcp-actions-backend.
139+
# experimentalDynamicClientRegistration:
140+
# enabled: true
141+
# allowedRedirectUriPatterns:
142+
# - '*'
98143
providers:
99144
# See https://backstage.io/docs/auth/guest/provider
100145
guest: {}
@@ -110,6 +155,19 @@ auth:
110155
# # Since we do not have a User entity, for local development, uncomment the following line to allow sign-in without a user in the catalog
111156
# dangerouslyAllowSignInWithoutUserInCatalog: true
112157

158+
# RBAC permission configuration.
159+
# Enables fine-grained role-based access control for Lightspeed and catalog.
160+
# Requires @backstage/plugin-permission-backend and @backstage-community/plugin-rbac-backend
161+
# in packages/backend/src/index.ts, plus an rbac-policy.csv file.
162+
# permission:
163+
# enabled: true
164+
# rbac:
165+
# policies-csv-file: ../../rbac-policy.csv
166+
# policyFileReload: true
167+
# admin:
168+
# superUsers:
169+
# - name: user:default/breanna.davison
170+
113171
scaffolder: {}
114172
# see https://backstage.io/docs/features/software-templates/configuration for software template options
115173

workspaces/lightspeed/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@
7676
"@backstage/frontend-plugin-api": "0.15.1",
7777
"infinispan": "0.13.0",
7878
"@protobufjs/inquire": "1.1.0",
79-
"langsmith": "^0.6.0"
79+
"langsmith": "^0.6.0",
80+
"@patternfly/react-core": "6.4.3"
8081
},
8182
"prettier": "@spotify/prettier-config",
8283
"lint-staged": {

workspaces/lightspeed/packages/app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"@backstage/frontend-plugin-api": "^0.15.1",
2828
"@backstage/integration-react": "^1.2.16",
2929
"@backstage/plugin-app-react": "^0.2.1",
30+
"@backstage/plugin-auth": "^0.1.7",
3031
"@backstage/plugin-catalog": "^2.0.1",
3132
"@backstage/plugin-scaffolder": "^1.36.1",
3233
"@backstage/plugin-search": "^1.7.0",

workspaces/lightspeed/packages/app/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { createApp } from '@backstage/frontend-defaults';
18+
import authPlugin from '@backstage/plugin-auth';
1819
import { appDrawerModule } from '@red-hat-developer-hub/backstage-plugin-app-react/alpha';
1920
import {
2021
lightspeedFABModule,
@@ -26,6 +27,7 @@ import { signInModule } from './modules/signIn';
2627

2728
export default createApp({
2829
features: [
30+
authPlugin,
2931
appDrawerModule,
3032
lightspeedFABModule,
3133
lightspeedTranslationsModule,

workspaces/lightspeed/packages/backend/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ backend.add(
3838
// See https://backstage.io/docs/features/software-catalog/configuration#subscribing-to-catalog-errors
3939
backend.add(import('@backstage/plugin-catalog-backend-module-logs'));
4040

41-
// RBAC backend (registers as "permission" and provides /rbac; do not add plugin-permission-backend separately)
41+
// permission plugin — RBAC backend v7+ no longer bundles this internally
42+
// (breaking change from v5/v6 where it self-registered as the permission provider).
43+
backend.add(import('@backstage/plugin-permission-backend'));
4244
backend.add(import('@backstage-community/plugin-rbac-backend'));
4345

4446
// search plugin

workspaces/lightspeed/plugins/lightspeed-backend/config.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ export interface Config {
4848
* @visibility secret
4949
*/
5050
token?: string;
51+
/**
52+
* Authentication mode for this MCP server.
53+
* Set to 'dcr' for Dynamic Client Registration (user-bound tokens minted per-request).
54+
* When omitted, falls back to static-token mode.
55+
* @visibility backend
56+
*/
57+
auth?: string;
5158
}>;
5259
/**
5360
* Configuration for AI Notebooks (Developer Preview)

workspaces/lightspeed/plugins/lightspeed-backend/report.api.md

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
44
55
```ts
6-
6+
import type { AuthService } from '@backstage/backend-plugin-api';
77
import { BackendFeature } from '@backstage/backend-plugin-api';
88
import type { Config } from '@backstage/config';
99
import type { DatabaseService } from '@backstage/backend-plugin-api';
@@ -22,55 +22,56 @@ export default lightspeedPlugin;
2222

2323
// @public
2424
export interface McpServerResponse {
25-
// (undocumented)
26-
enabled: boolean;
27-
// (undocumented)
28-
hasToken: boolean;
29-
// (undocumented)
30-
hasUserToken: boolean;
31-
// (undocumented)
32-
name: string;
33-
// (undocumented)
34-
status: McpServerStatus;
35-
// (undocumented)
36-
toolCount: number;
37-
// (undocumented)
38-
url?: string;
25+
auth?: string;
26+
// (undocumented)
27+
enabled: boolean;
28+
// (undocumented)
29+
hasToken: boolean;
30+
// (undocumented)
31+
hasUserToken: boolean;
32+
// (undocumented)
33+
name: string;
34+
// (undocumented)
35+
status: McpServerStatus;
36+
// (undocumented)
37+
toolCount: number;
38+
// (undocumented)
39+
url?: string;
3940
}
4041

4142
// @public (undocumented)
4243
export type McpServerStatus = 'connected' | 'error' | 'unknown';
4344

4445
// @public (undocumented)
4546
export interface McpToolInfo {
46-
// (undocumented)
47-
description: string;
48-
// (undocumented)
49-
name: string;
47+
// (undocumented)
48+
description: string;
49+
// (undocumented)
50+
name: string;
5051
}
5152

5253
// @public (undocumented)
5354
export interface McpValidationResult {
54-
// (undocumented)
55-
error?: string;
56-
// (undocumented)
57-
toolCount: number;
58-
// (undocumented)
59-
tools: McpToolInfo[];
60-
// (undocumented)
61-
valid: boolean;
55+
// (undocumented)
56+
error?: string;
57+
// (undocumented)
58+
toolCount: number;
59+
// (undocumented)
60+
tools: McpToolInfo[];
61+
// (undocumented)
62+
valid: boolean;
6263
}
6364

6465
// @public
6566
export type RouterOptions = {
66-
logger: LoggerService;
67-
config: Config;
68-
database: DatabaseService;
69-
httpAuth: HttpAuthService;
70-
userInfo: UserInfoService;
71-
permissions: PermissionsService;
67+
logger: LoggerService;
68+
config: Config;
69+
database: DatabaseService;
70+
httpAuth: HttpAuthService;
71+
userInfo: UserInfoService;
72+
permissions: PermissionsService;
73+
auth: AuthService;
7274
};
7375

7476
// (No @packageDocumentation comment for this package)
75-
7677
```

workspaces/lightspeed/plugins/lightspeed-backend/src/plugin.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const lightspeedPlugin = createBackendPlugin({
3636
config: coreServices.rootConfig,
3737
http: coreServices.httpRouter,
3838
httpAuth: coreServices.httpAuth,
39+
auth: coreServices.auth,
3940
userInfo: coreServices.userInfo,
4041
permissions: coreServices.permissions,
4142
database: coreServices.database,
@@ -45,6 +46,7 @@ export const lightspeedPlugin = createBackendPlugin({
4546
config,
4647
http,
4748
httpAuth,
49+
auth,
4850
userInfo,
4951
permissions,
5052
database,
@@ -57,6 +59,7 @@ export const lightspeedPlugin = createBackendPlugin({
5759
logger,
5860
database,
5961
httpAuth,
62+
auth,
6063
userInfo,
6164
permissions,
6265
}),

0 commit comments

Comments
 (0)