@mcp-abap-adt/proxy 2.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +181 -0
- package/LICENSE +669 -17
- package/README.md +79 -8
- package/bin/mcp-abap-adt-proxy-mcp.js +113 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +54 -97
- package/dist/lib/stores.d.ts +23 -2
- package/dist/lib/stores.d.ts.map +1 -1
- package/dist/lib/stores.js +56 -1
- package/dist/mcp/cli.d.ts +2 -0
- package/dist/mcp/cli.d.ts.map +1 -0
- package/dist/mcp/cli.js +21 -0
- package/dist/mcp/configs.d.ts +33 -0
- package/dist/mcp/configs.d.ts.map +1 -0
- package/dist/mcp/configs.js +142 -0
- package/dist/mcp/ports.d.ts +15 -0
- package/dist/mcp/ports.d.ts.map +1 -0
- package/dist/mcp/ports.js +35 -0
- package/dist/mcp/registry.d.ts +57 -0
- package/dist/mcp/registry.d.ts.map +1 -0
- package/dist/mcp/registry.js +145 -0
- package/dist/mcp/server.d.ts +34 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +79 -0
- package/dist/mcp/shutdown.d.ts +37 -0
- package/dist/mcp/shutdown.d.ts.map +1 -0
- package/dist/mcp/shutdown.js +82 -0
- package/dist/mcp/supervisor.d.ts +125 -0
- package/dist/mcp/supervisor.d.ts.map +1 -0
- package/dist/mcp/supervisor.js +330 -0
- package/dist/mcp/tools.d.ts +28 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +152 -0
- package/dist/proxy/btpProxy.d.ts +24 -73
- package/dist/proxy/btpProxy.d.ts.map +1 -1
- package/dist/proxy/btpProxy.js +65 -616
- package/dist/proxy/credentials.d.ts +45 -0
- package/dist/proxy/credentials.d.ts.map +1 -0
- package/dist/proxy/credentials.js +41 -0
- package/dist/proxy/requestHandler.d.ts +38 -0
- package/dist/proxy/requestHandler.d.ts.map +1 -0
- package/dist/proxy/requestHandler.js +73 -0
- package/dist/proxy/reverseProxy.d.ts +11 -2
- package/dist/proxy/reverseProxy.d.ts.map +1 -1
- package/dist/proxy/reverseProxy.js +52 -9
- package/dist/router/headerAnalyzer.js +2 -2
- package/dist/router/requestInterceptor.js +9 -9
- package/docs/API.md +172 -0
- package/docs/ARCHITECTURE.md +322 -0
- package/docs/CLIENT_SETUP.md +413 -0
- package/docs/CONFIGURATION.md +258 -0
- package/docs/MIGRATION-4.0.md +125 -0
- package/docs/ROUTING_LOGIC.md +126 -0
- package/docs/TROUBLESHOOTING.md +488 -0
- package/docs/USAGE.md +422 -0
- package/docs/YAML_CONFIG.md +273 -0
- package/docs/mcp-proxy-config.example.yaml +62 -0
- package/package.json +17 -10
- package/dist/proxy/cloudLlmHubProxy.d.ts +0 -2
- package/dist/proxy/cloudLlmHubProxy.d.ts.map +0 -1
- package/dist/proxy/cloudLlmHubProxy.js +0 -3
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Migration to 4.0
|
|
2
|
+
|
|
3
|
+
Two things changed that a consumer can see, and one that only a programmatic
|
|
4
|
+
consumer can. Everything else in this release is internal.
|
|
5
|
+
|
|
6
|
+
If you run the proxy from a config file and point a client at it, **read §1 and
|
|
7
|
+
stop there** — that is very likely all that applies to you.
|
|
8
|
+
|
|
9
|
+
## 1. Dependencies: the contracts moved out of the umbrella
|
|
10
|
+
|
|
11
|
+
`@mcp-abap-adt/interfaces` is gone from this package's dependencies. The
|
|
12
|
+
contracts now come from the packages they live in:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
@mcp-abap-adt/interfaces ^7.0.0 → removed
|
|
16
|
+
@mcp-abap-adt/interfaces-network (new) → ^2.0.0 every HTTP header constant
|
|
17
|
+
@mcp-abap-adt/interfaces-auth (new) → ^1.2.0 ITokenRefresher
|
|
18
|
+
@mcp-abap-adt/interfaces-auth-sap (new) → ^1.0.0 IAuthorizationConfig
|
|
19
|
+
@mcp-abap-adt/connection (new) → ^9.2.0 TokenAuthProvider
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The sibling packages moved with them — `auth-broker` to `^2.2.0` (a major),
|
|
23
|
+
`auth-providers` `^2.2.2`, `auth-stores` `^1.2.0`, `header-validator` `^0.3.0`,
|
|
24
|
+
`logger` `^0.4.0` — because each has left the umbrella too. Nothing in this
|
|
25
|
+
package's tree asks for `@mcp-abap-adt/interfaces` any more.
|
|
26
|
+
|
|
27
|
+
**What you do:** if you install this package and nothing else, nothing. If your
|
|
28
|
+
own code imported a contract type and happened to resolve it through this
|
|
29
|
+
package's tree, install the package that now declares it. The old name still
|
|
30
|
+
exists on npm and still re-exports everything, but every one of those exports is
|
|
31
|
+
marked `@deprecated`.
|
|
32
|
+
|
|
33
|
+
## 2. An SSE response streams, and carries your headers
|
|
34
|
+
|
|
35
|
+
This only matters if you use `transport: sse`. The `streamable-http` and `stdio`
|
|
36
|
+
transports are unaffected.
|
|
37
|
+
|
|
38
|
+
The SSE transport used to rebuild each request by hand, carry it over axios,
|
|
39
|
+
buffer the whole answer and rewrap it as a JSON-RPC envelope. It now goes through
|
|
40
|
+
the same transparent pipe as every other transport. Three consequences:
|
|
41
|
+
|
|
42
|
+
**The response arrives as it is produced**, not all at once at the end. This is
|
|
43
|
+
the point of the change.
|
|
44
|
+
|
|
45
|
+
**Your headers reach the target.** The old path forwarded none of them and
|
|
46
|
+
imposed two of its own:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Accept: application/json, application/x-ndjson, text/event-stream
|
|
50
|
+
Content-Type: application/json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
It no longer does. The proxy is transparent and answers for the `Authorization`
|
|
54
|
+
header only. If your target requires a particular `Accept`, put it in that
|
|
55
|
+
proxy's `defaultHeaders`, where it is your choice and visible in your config:
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
defaultHeaders:
|
|
59
|
+
accept: "application/json, text/event-stream"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
A header your client sends always wins over `defaultHeaders`.
|
|
63
|
+
|
|
64
|
+
**The upstream path is your client's path.** The old SSE path had three branches:
|
|
65
|
+
with `targetUrl` set it used base + your client's path; with a service-key URL
|
|
66
|
+
already containing `/mcp` it used that URL as-is and *discarded* your client's
|
|
67
|
+
path; otherwise it appended `/mcp/stream/http`. Now it is always base + your
|
|
68
|
+
client's path.
|
|
69
|
+
|
|
70
|
+
**What you do:** if your config sets `targetUrl`, nothing — that is the branch
|
|
71
|
+
the old code took and it behaved the same way. If it relies on the service key's
|
|
72
|
+
own `abap.url`, set `targetUrl` explicitly to the URL you actually want.
|
|
73
|
+
|
|
74
|
+
## 3. `BtpProxy.getJwtToken()` is replaced, not renamed
|
|
75
|
+
|
|
76
|
+
Only for code that imports `BtpProxy` directly.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// before
|
|
80
|
+
const token = await proxy.getJwtToken(destination);
|
|
81
|
+
forward(req, res, url, token); // composed `Bearer ${token}` itself
|
|
82
|
+
|
|
83
|
+
// after
|
|
84
|
+
const authorization = await proxy.getAuthorizationHeader(destination);
|
|
85
|
+
forward(req, res, url, authorization); // a complete header VALUE, or null
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`getAuthorizationHeader()` answers the whole header value — `Bearer <token>` — or
|
|
89
|
+
`null` where the credential is not a header at all, which a certificate is not.
|
|
90
|
+
Pass it through as it stands: composing `Bearer` around it sends
|
|
91
|
+
`Bearer Bearer <token>`, and turning `null` into `''` sends an empty
|
|
92
|
+
`Authorization`, which is a different claim from having none.
|
|
93
|
+
|
|
94
|
+
Gone with it: `proxyRequest()`, `buildProxyRequest()`, and the `ProxyRequest` /
|
|
95
|
+
`ProxyResponse` types. They described the envelope the axios path built; there is
|
|
96
|
+
no envelope any more.
|
|
97
|
+
|
|
98
|
+
## 4. The circuit breaker is gone
|
|
99
|
+
|
|
100
|
+
It guarded the buffered forward. The forwarding path streams, and a breaker there
|
|
101
|
+
would mean buffering the response again — the thing being fixed. A target that
|
|
102
|
+
keeps failing now fails visibly on every request instead of being
|
|
103
|
+
short-circuited.
|
|
104
|
+
|
|
105
|
+
`circuitBreakerThreshold`, `circuitBreakerTimeout` and
|
|
106
|
+
`MCP_PROXY_CIRCUIT_BREAKER_*` are still read, so existing configuration loads
|
|
107
|
+
unchanged. They do nothing.
|
|
108
|
+
|
|
109
|
+
Retry is unaffected and still applies to getting a token: 5xx and network
|
|
110
|
+
failures are retried with exponential backoff. A missing service key is not
|
|
111
|
+
retried — it answers at once, naming the file to create.
|
|
112
|
+
|
|
113
|
+
## What did not change
|
|
114
|
+
|
|
115
|
+
The routing rules, `x-sap-destination` / `--btp`, `x-target-url` /
|
|
116
|
+
`--target-url`, `defaultHeaders`, `${VAR}` interpolation, `envFile`, the service
|
|
117
|
+
key layout, the transports on offer, and every configuration key except the two
|
|
118
|
+
named above.
|
|
119
|
+
|
|
120
|
+
## New, and entirely optional
|
|
121
|
+
|
|
122
|
+
A second command, `mcp-abap-adt-proxy-mcp`, speaks MCP over stdio and its tools
|
|
123
|
+
start and stop proxies from the configs in `~/.config/mcp-abap-adt/proxy/`.
|
|
124
|
+
Nothing about the existing command changes because it exists — see
|
|
125
|
+
[Usage](./USAGE.md#the-management-mode).
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Routing Logic Specification
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The proxy routes requests to MCP servers (local or on BTP) and handles BTP/XSUAA authentication.
|
|
6
|
+
|
|
7
|
+
## Key Principles
|
|
8
|
+
|
|
9
|
+
1. **No .env files in proxy**: Proxy should NOT use `.env` files for connection configuration. Only destinations via auth-broker (service key files) are used.
|
|
10
|
+
2. **Validation only for destination**: Proxy validates only the `x-sap-destination` header. Other headers are passed directly to MCP server.
|
|
11
|
+
3. **Destination priority**: Destination can be specified via command-line parameter (`--btp`) or header (`x-sap-destination`). Parameter takes precedence.
|
|
12
|
+
4. **Header passthrough**: All other headers (except `x-sap-destination`) are passed directly to MCP server without validation.
|
|
13
|
+
|
|
14
|
+
## Routing Scenarios
|
|
15
|
+
|
|
16
|
+
### Scenario 1: BTP MCP Server (`--btp`)
|
|
17
|
+
|
|
18
|
+
**Configuration:**
|
|
19
|
+
- `--btp=<btp-destination>` (or header `x-sap-destination`) - BTP destination for authentication
|
|
20
|
+
|
|
21
|
+
**Behavior:**
|
|
22
|
+
- MCP server URL: From BTP destination service key (`abap.url` field)
|
|
23
|
+
- **BTP Authentication**: Uses `btpAuthBroker` with `ClientCredentialsProvider` to get BTP Cloud token
|
|
24
|
+
- Token obtained from BTP destination service key (contains `uaa.url`, `uaa.clientid`, `uaa.clientsecret`)
|
|
25
|
+
- Injects/overwrites `Authorization: Bearer <token>` header
|
|
26
|
+
|
|
27
|
+
**Use Case:** Production/cloud deployment with MCP server on BTP
|
|
28
|
+
|
|
29
|
+
**Example:**
|
|
30
|
+
```bash
|
|
31
|
+
mcp-abap-adt-proxy --btp=btp-cloud
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Scenario 2: BTP MCP Server with explicit target URL (`--btp` + `--target-url`)
|
|
35
|
+
|
|
36
|
+
**Configuration:**
|
|
37
|
+
- `--btp=<btp-destination>` (or header `x-sap-destination`) - BTP destination for authentication
|
|
38
|
+
- `--target-url=<url>` (or header `x-target-url`) - Override the target URL
|
|
39
|
+
|
|
40
|
+
**Behavior:**
|
|
41
|
+
- MCP server URL: From `--target-url` (overrides the `abap.url` from the service key)
|
|
42
|
+
- **BTP Authentication**: Auth token still comes from the `--btp` destination service key
|
|
43
|
+
- Injects/overwrites `Authorization: Bearer <token>` header
|
|
44
|
+
|
|
45
|
+
**Use Case:** Auth comes from one service key, but requests must go to a different URL
|
|
46
|
+
(e.g. direct OData testing or a non-standard MCP path)
|
|
47
|
+
|
|
48
|
+
**Example:**
|
|
49
|
+
```bash
|
|
50
|
+
mcp-abap-adt-proxy --btp=btp-cloud \
|
|
51
|
+
--target-url=https://mcp-server.cfapps.eu10.hana.ondemand.com
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> **Note:** Direct unauthenticated routing via `--mcp-url` / `x-mcp-url` has been
|
|
55
|
+
> **removed**. A BTP destination (`--btp` or `x-sap-destination`) is required; without
|
|
56
|
+
> it the proxy cannot resolve a target or obtain a token.
|
|
57
|
+
|
|
58
|
+
## Header Mapping
|
|
59
|
+
|
|
60
|
+
### Validated Headers
|
|
61
|
+
|
|
62
|
+
| Header | Source | Description |
|
|
63
|
+
|--------|--------|-------------|
|
|
64
|
+
| `x-sap-destination` | Request header or `--btp` parameter | BTP destination name for authentication |
|
|
65
|
+
|
|
66
|
+
**Note:** This is the ONLY header that proxy validates. All other headers are passed directly to MCP server.
|
|
67
|
+
|
|
68
|
+
### Headers Added by Proxy
|
|
69
|
+
|
|
70
|
+
| Source | Target Header | Description |
|
|
71
|
+
|--------|---------------|-------------|
|
|
72
|
+
| BTP destination token (via `btpAuthBroker` with `ClientCredentialsProvider`) | `Authorization: Bearer <token>` | For MCP server on BTP (when `x-sap-destination` or `--btp` is provided) |
|
|
73
|
+
|
|
74
|
+
### Headers Passed Through
|
|
75
|
+
|
|
76
|
+
All headers from the original request (except `x-sap-destination`) are passed directly to MCP server without modification. This includes any custom headers the MCP server might need.
|
|
77
|
+
|
|
78
|
+
## Current Implementation Analysis
|
|
79
|
+
|
|
80
|
+
### What Works Correctly
|
|
81
|
+
|
|
82
|
+
1. **BTP AuthBroker architecture**: `btpAuthBroker` with `ClientCredentialsProvider` for BTP destinations (service keys with `uaa` section)
|
|
83
|
+
2. **BTP destination handling**: Correctly gets token from BTP destination using `btpAuthBroker` for MCP server authorization
|
|
84
|
+
3. **Service key store**: `XsuaaServiceKeyStore` correctly reads service keys for BTP destinations
|
|
85
|
+
4. **Command-line overrides**: `--btp`, `--target-url` work as expected
|
|
86
|
+
5. **Header passthrough**: Original headers are preserved and forwarded to MCP server
|
|
87
|
+
6. **Platform path resolution**: Correctly determines service key and session paths for Unix and Windows
|
|
88
|
+
|
|
89
|
+
## Testing Scenarios
|
|
90
|
+
|
|
91
|
+
### Test 1: BTP MCP with explicit target URL
|
|
92
|
+
```bash
|
|
93
|
+
# Start proxy
|
|
94
|
+
mcp-abap-adt-proxy --btp=btp-cloud \
|
|
95
|
+
--target-url=https://mcp-server.cfapps.eu10.hana.ondemand.com
|
|
96
|
+
|
|
97
|
+
# Send request with headers
|
|
98
|
+
curl -X POST http://localhost:3001/mcp/stream/http \
|
|
99
|
+
-H "Content-Type: application/json" \
|
|
100
|
+
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Expected:**
|
|
104
|
+
- Request forwarded to the `--target-url` value with `Authorization: Bearer <btp-token>`
|
|
105
|
+
|
|
106
|
+
### Test 2: BTP MCP with BTP Destination
|
|
107
|
+
```bash
|
|
108
|
+
# Start proxy
|
|
109
|
+
mcp-abap-adt-proxy --btp=btp-cloud
|
|
110
|
+
|
|
111
|
+
# Send request
|
|
112
|
+
curl -X POST http://localhost:3001/mcp/stream/http \
|
|
113
|
+
-H "Content-Type: application/json" \
|
|
114
|
+
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Expected:**
|
|
118
|
+
- MCP server receives: `Authorization: Bearer <btp-token>` (from BTP destination)
|
|
119
|
+
- MCP server URL obtained from BTP destination service key
|
|
120
|
+
|
|
121
|
+
## Summary
|
|
122
|
+
|
|
123
|
+
The proxy supports flexible routing:
|
|
124
|
+
- **BTP Authentication Mode**: Use `--btp` for BTP authentication, MCP URL from service key
|
|
125
|
+
- **Explicit URL Mode**: Use `--btp` + `--target-url` for BTP auth with an overridden target URL
|
|
126
|
+
- **No .env files**: Only use destinations via auth-broker, never .env files
|