Skip to content

Commit ff39f9a

Browse files
authored
feat(x2a): X2A MCP tools (#2733)
* feat(x2a): X2A MCP tools - Add x2a-mcp-extras backend plugin with MCP tools - Add x2a-dcr frontend plugin: legacy-frontend reimplementation of @backstage/plugin-auth for RHDH 1.9 DCR consent flow (to be replaced by upstream plugin in RHDH 1.10+) * x2a-dcr plugin * Move X2AConfig to types.ts * Move shared backend code to x2a-node * avoid orphaned init jobs and other fixes * x2a-create-project instructs about init-phase run * x2a-trigger-next-phase links to manual action * cleanup * share RBAC checks between MCP and HTTP * Add #runinit and #runnext deeplinks to x2a/projects/[projectid] URL * Update documentation for authZ resolution in MCP tools
1 parent 9ca292c commit ff39f9a

File tree

85 files changed

+5375
-509
lines changed

Some content is hidden

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

85 files changed

+5375
-509
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-x2a-mcp-extras': minor
3+
'@red-hat-developer-hub/backstage-plugin-x2a-backend': minor
4+
'@red-hat-developer-hub/backstage-plugin-x2a-dcr': minor
5+
'@red-hat-developer-hub/backstage-plugin-x2a-node': minor
6+
'@red-hat-developer-hub/backstage-plugin-x2a-common': patch
7+
'@red-hat-developer-hub/backstage-plugin-x2a': patch
8+
---
9+
10+
Add x2a-mcp-extras backend plugin exposing MCP tools for AI clients, x2a-dcr frontend plugin for RHDH 1.9 DCR OAuth consent flow, and x2a-node shared node-library. Refactor x2a-backend to use shared service refs from x2a-node via a feature loader.

workspaces/x2a/app-config.yaml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ backend:
2222
# Content-Security-Policy directives follow the Helmet format: https://helmetjs.github.io/#reference
2323
# Default Helmet Content-Security-Policy values can be removed by setting the key to false
2424
cors:
25-
origin: http://localhost:3000
25+
origin:
26+
- http://localhost:3000
27+
- http://localhost:6274
2628
methods: [GET, HEAD, PATCH, POST, PUT, DELETE]
2729
credentials: true
2830
# This is for local development only, it is not recommended to use this in production
@@ -32,6 +34,22 @@ backend:
3234
connection: ':memory:'
3335
# workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir
3436

37+
# MCP Actions: expose x2a tools to AI clients (Cursor, Claude, etc.)
38+
actions:
39+
pluginSources:
40+
- 'catalog'
41+
- 'software-catalog-mcp-tool'
42+
- 'x2a-mcp-extras'
43+
# Enable following for non-DCR flow for MCP tools
44+
# auth:
45+
# externalAccess:
46+
# - type: static
47+
# options:
48+
# token: ${MCP_TOKEN}
49+
# subject: mcp-clients
50+
# accessRestrictions:
51+
# - plugin: mcp-actions
52+
3553
proxy:
3654
### Example for how to add a proxy endpoint for the frontend.
3755
### A typical reason to do this is to handle HTTPS and CORS for internal services.
@@ -74,6 +92,17 @@ integrations:
7492
auth:
7593
# see https://backstage.io/docs/auth/ to learn about auth providers
7694
environment: development
95+
# Uncomment to enable OAuth DCR for MCP clients (user-identity auth).
96+
# Narrow allowedRedirectUriPatterns to match your specific needs.
97+
# Requires either @backstage/plugin-auth or x2a-dcr frontend plugin.
98+
experimentalDynamicClientRegistration:
99+
enabled: true
100+
allowedRedirectUriPatterns:
101+
# Keeping those generic for development. Narrow to specific patterns in production.
102+
- cursor://*
103+
- https://*
104+
- http://*
105+
# - http://localhost:*
77106
providers:
78107
# See https://backstage.io/docs/auth/guest/provider
79108
guest: {}

workspaces/x2a/examples/example-rbac-policy.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ g, user:development/guest, role:default/x2aUser
1717

1818
# the account to play with the admin roles
1919
#g, user:default/mareklibra, role:default/x2aViewerAdmin
20+
g, user:default/mareklibra, role:default/x2aAdmin
2021
g, group:default/x2a-admin-group, role:default/x2aAdmin
2122

2223
g, user:default/elai-shalev, role:default/x2aAdmin

workspaces/x2a/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717
"openapi-generate": "cd plugins/x2a-backend && yarn openapi-generate",
1818
"tsc": "tsc",
1919
"tsc:full": "tsc --skipLibCheck true --incremental false",
20-
"clean": "backstage-cli repo clean",
20+
"clean": "backstage-cli repo clean && find plugins -mindepth 2 -maxdepth 2 -type d -name dist-dynamic -exec rm -rf {} +",
2121
"test": "backstage-cli repo test",
2222
"test:pg": "CI=true backstage-cli repo test",
2323
"test:all": "yarn openapi-generate && yarn prettier:check && yarn lint:all && backstage-cli repo test --coverage",
2424
"test:all:pg": "yarn openapi-generate && yarn prettier:check && yarn lint:all && CI=true backstage-cli repo test --coverage",
2525
"test:e2e": "echo Skipping until we have tests: playwright test",
26-
"fix": "backstage-cli repo fix",
26+
"fix": "backstage-cli repo fix --publish",
2727
"lint": "backstage-cli repo lint --since origin/main",
2828
"lint:all": "backstage-cli repo lint",
2929
"prettier:check": "prettier --check .",

workspaces/x2a/packages/app/e2e-tests/app.test.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,3 @@ import { test, expect } from '@playwright/test';
2020
test('noop test', async () => {
2121
expect(true).toBe(true);
2222
});
23-
24-
/*
25-
test('App should render the welcome page', async ({ page }) => {
26-
await page.goto('/');
27-
28-
const enterButton = page.getByRole('button', { name: 'Enter' });
29-
await expect(enterButton).toBeVisible();
30-
await enterButton.click();
31-
32-
await expect(page.getByText('My Company Catalog')).toBeVisible();
33-
});
34-
*/

workspaces/x2a/packages/app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"@material-ui/core": "^4.12.2",
5252
"@material-ui/icons": "^4.9.1",
5353
"@red-hat-developer-hub/backstage-plugin-x2a": "workspace:*",
54+
"@red-hat-developer-hub/backstage-plugin-x2a-dcr": "workspace:*",
5455
"react": "^18.0.2",
5556
"react-dom": "^18.0.2",
5657
"react-router": "^6.3.0",

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import {
5959
RepoAuthenticationExtension,
6060
X2ARepoUrlPickerExtension,
6161
} from '@red-hat-developer-hub/backstage-plugin-x2a';
62+
import { DcrConsentPage } from '@red-hat-developer-hub/backstage-plugin-x2a-dcr';
6263
import {
6364
bitbucketAuthApiRef,
6465
githubAuthApiRef,
@@ -154,6 +155,7 @@ const routes = (
154155
<Route path="/settings" element={<UserSettingsPage />} />
155156
<Route path="/catalog-graph" element={<CatalogGraphPage />} />
156157
<Route path="/notifications" element={<NotificationsPage />} />
158+
<Route path="/oauth2/*" element={<DcrConsentPage />} />
157159

158160
{/* At RHDH runtime, this is replaced by dynamicPlugin configuration:
159161
https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.9/html/installing_and_viewing_plugins_in_red_hat_developer_hub/assembly-front-end-plugin-wiring.adoc_rhdh-extensions-plugins#con-providing-custom-scaffolder-field-extensions.adoc_assembly-front-end-plugin-wiring
Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,47 @@
1-
# example-backend
1+
# x2a backend
22

3-
This package is an EXAMPLE of a Backstage backend.
3+
Backstage backend for local development of the **x2a** workspace.
44

5-
The main purpose of this package is to provide a test bed for Backstage plugins
6-
that have a backend part. Feel free to experiment locally or within your fork by
7-
adding dependencies and routes to this backend, to try things out.
5+
This package wires together the x2a plugins and the standard Backstage
6+
infrastructure so you can run a fully functional backend on your machine.
87

9-
Our goal is to eventually amend the create-app flow of the CLI, such that a
10-
production ready version of a backend skeleton is made alongside the frontend
11-
app. Until then, feel free to experiment here!
8+
## x2a service factories
129

13-
## Development
10+
The x2a plugins share service refs defined in
11+
`@red-hat-developer-hub/backstage-plugin-x2a-node` (the `x2a-node`
12+
node-library). Those refs carry **no `defaultFactory`**.
1413

15-
To run the example backend, first go to the project root and run
14+
The `x2a-backend` default export is a `createBackendFeatureLoader` that yields
15+
both the plugin and the matching service factories (`x2aDatabaseServiceFactory`,
16+
`kubeServiceFactory`). This means a single `backend.add(...)` call registers
17+
everything:
1618

17-
```bash
18-
yarn install
19+
```ts
20+
backend.add(import('@red-hat-developer-hub/backstage-plugin-x2a-backend'));
1921
```
2022

21-
You should only need to do this once.
23+
This design works in both local development and in RHDH dynamic plugin mode,
24+
because RHDH's dynamic plugin loader only reads the default export of each
25+
plugin package.
2226

23-
After that, go to the `packages/backend` directory and run
27+
## Development
28+
29+
From the workspace root:
2430

2531
```bash
32+
yarn install
2633
yarn start
2734
```
2835

29-
If you want to override any configuration locally, for example adding any secrets,
30-
you can do so in `app-config.local.yaml`.
31-
32-
The backend starts up on port 7007 per default.
33-
34-
## Populating The Catalog
35-
36-
If you want to use the catalog functionality, you need to add so called
37-
locations to the backend. These are places where the backend can find some
38-
entity descriptor data to consume and serve. For more information, see
39-
[Software Catalog Overview - Adding Components to the Catalog](https://backstage.io/docs/features/software-catalog/#adding-components-to-the-catalog).
40-
41-
To get started quickly, this template already includes some statically configured example locations
42-
in `app-config.yaml` under `catalog.locations`. You can remove and replace these locations as you
43-
like, and also override them for local development in `app-config.local.yaml`.
44-
45-
## Authentication
36+
Override secrets or local configuration in `app-config.local.yaml`.
37+
The backend starts on port **7007** by default.
4638

47-
We chose [Passport](http://www.passportjs.org/) as authentication platform due
48-
to its comprehensive set of supported authentication
49-
[strategies](http://www.passportjs.org/packages/).
39+
## Populating the catalog
5040

51-
Read more about the
52-
[auth-backend](https://github.com/backstage/backstage/blob/master/plugins/auth-backend/README.md)
53-
and
54-
[how to add a new provider](https://github.com/backstage/backstage/blob/master/docs/auth/add-auth-provider.md)
41+
Add location entries under `catalog.locations` in `app-config.yaml`.
42+
See [Adding Components to the Catalog](https://backstage.io/docs/features/software-catalog/#adding-components-to-the-catalog)
43+
for details.
5544

5645
## Documentation
5746

58-
- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md)
5947
- [Backstage Documentation](https://backstage.io/docs)

workspaces/x2a/packages/backend/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"@backstage/plugin-catalog-backend-module-logs": "0.1.16",
3636
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.2.14",
3737
"@backstage/plugin-kubernetes-backend": "0.20.4",
38+
"@backstage/plugin-mcp-actions-backend": "0.1.9",
3839
"@backstage/plugin-notifications-backend": "0.6.0",
3940
"@backstage/plugin-permission-backend": "0.7.3",
4041
"@backstage/plugin-permission-common": "^0.9.3",
@@ -54,6 +55,7 @@
5455
"@backstage/plugin-techdocs-backend": "2.1.2",
5556
"@red-hat-developer-hub/backstage-plugin-scaffolder-backend-module-x2a": "workspace:^",
5657
"@red-hat-developer-hub/backstage-plugin-x2a-backend": "workspace:*",
58+
"@red-hat-developer-hub/backstage-plugin-x2a-mcp-extras": "workspace:^",
5759
"app": "link:../app",
5860
"better-sqlite3": "^12.0.0",
5961
"node-gyp": "^9.0.0",

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,16 @@ backend.add(import('@backstage/plugin-kubernetes-backend'));
8484
backend.add(import('@backstage/plugin-notifications-backend'));
8585
backend.add(import('@backstage/plugin-signals-backend'));
8686

87+
// x2a-backend's default export is a feature loader that yields the plugin
88+
// together with service factories for the x2a-node canonical refs.
8789
backend.add(import('@red-hat-developer-hub/backstage-plugin-x2a-backend'));
8890

8991
backend.add(
9092
import('@red-hat-developer-hub/backstage-plugin-scaffolder-backend-module-x2a'),
9193
);
94+
95+
// MCP server + x2a MCP tools
96+
backend.add(import('@backstage/plugin-mcp-actions-backend'));
97+
backend.add(import('@red-hat-developer-hub/backstage-plugin-x2a-mcp-extras'));
98+
9299
backend.start();

0 commit comments

Comments
 (0)