Skip to content

Commit 81f799a

Browse files
committed
Prepare AWS deployment scripts
1 parent c73a62c commit 81f799a

8 files changed

Lines changed: 1039 additions & 8 deletions

File tree

README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ docker run -i --rm \
126126
| `MCP_PORT` | No | `8000` | Port to bind the HTTP server (HTTP mode only). |
127127
| `MCP_VERBOSE` | No | `false` | Enable verbose debug logging (`true`, `1`, or `yes`). Logs raw API requests/responses. Can also be set via `-v` CLI flag. |
128128

129+
> **Note on HTTP deployments (e.g. AWS):** When running in HTTP mode over a network, you can skip setting `NETLICENSING_API_KEY` on the server. The MCP server will automatically extract the key per-request if the connecting client provides it via the `X-NetLicensing-API-Key` HTTP header, `Authorization: Bearer <key>` header, or `?apikey=<key>` query parameter.
130+
129131
---
130132

131133
### Claude Desktop
@@ -286,6 +288,51 @@ ngrok http 8000
286288
# Then point your client at the generated HTTPS URL
287289
```
288290

291+
### Cloud Deployment (AWS)
292+
293+
Deploy the MCP server to AWS for a **public HTTPS endpoint** that remote AI agents can connect to:
294+
295+
| Option | Best for | Scale-to-zero | Setup |
296+
|---|---|---|---|
297+
| **ECS Fargate** | Production, consistent traffic | No | ALB + Fargate |
298+
| **App Runner** | Low-traffic, development | ✅ Yes | Auto-provisioned HTTPS |
299+
300+
#### Quick deploy with the helper script
301+
302+
```bash
303+
cd deploy/aws
304+
305+
# Option A — ECS Fargate
306+
# Deploy (with optional HTTPS via ACM certificate)
307+
./deploy.sh fargate \
308+
--certificate-arn arn:aws:acm:us-east-1:123456789:certificate/your-cert-id
309+
310+
# Option B — App Runner (mirror image to ECR first)
311+
./deploy.sh mirror --ecr-repo 123456789.dkr.ecr.us-east-1.amazonaws.com/netlicensing-mcp
312+
./deploy.sh apprunner \
313+
--ecr-image 123456789.dkr.ecr.us-east-1.amazonaws.com/netlicensing-mcp:latest
314+
```
315+
316+
#### Connect MCP clients to the public URL
317+
318+
To use a shared, remote deployment, do not bake the `NETLICENSING_API_KEY` into the remote server settings. Instead, pass the API key from the client side using query parameters or HTTP headers.
319+
320+
For example, using the URL configuration in your MCP client with a query parameter:
321+
322+
```json
323+
{
324+
"mcpServers": {
325+
"netlicensing": {
326+
"url": "https://your-endpoint.us-east-1.elb.amazonaws.com/mcp?apikey=YOUR_API_KEY"
327+
}
328+
}
329+
}
330+
```
331+
332+
If you use a client or tool that supports passing HTTP headers directly, you can alternatively provide the key via `X-NetLicensing-API-Key: YOUR_API_KEY` or `Authorization: Bearer YOUR_API_KEY`.
333+
334+
📖 Full instructions, architecture diagrams, and cost estimates: **[deploy/aws/README.md](deploy/aws/README.md)**
335+
289336
---
290337

291338
## Contributing

deploy/aws/README.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# AWS Deployment — NetLicensing MCP Server
2+
3+
Deploy the NetLicensing MCP server to AWS with a **public HTTPS URL** so remote
4+
AI agents (Claude, Copilot, custom apps) can connect via the
5+
`streamable-http` transport.
6+
7+
Two deployment options are provided:
8+
9+
| Option | Best for | Cost (idle) | Scale-to-zero | HTTPS |
10+
|---|---|---|---|---|
11+
| **ECS Fargate** | Production, full control | ~$5–15/mo (Spot) | No (min 1 task) | Via ALB + ACM cert |
12+
| **App Runner** | Simplest, low-traffic | ~$0 (scale-to-zero) | ✅ Yes | Auto-provisioned |
13+
14+
---
15+
16+
## Prerequisites
17+
18+
- **AWS CLI v2**[Install guide](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
19+
- **AWS credentials** configured (`aws configure` or environment variables)
20+
- **Docker** (only needed if mirroring images to ECR for App Runner)
21+
22+
---
23+
24+
## Option A — ECS Fargate (recommended for production)
25+
26+
### 1. Deploy the stack
27+
28+
```bash
29+
# HTTP only (quick test)
30+
./deploy.sh fargate
31+
32+
# HTTPS (production) — requires an ACM certificate
33+
./deploy.sh fargate \
34+
--certificate-arn arn:aws:acm:us-east-1:123456789:certificate/your-cert-id
35+
```
36+
37+
Or use CloudFormation directly:
38+
39+
```bash
40+
aws cloudformation deploy \
41+
--template-file deploy/aws/ecs-fargate.yaml \
42+
--stack-name netlicensing-mcp \
43+
--region us-east-1 \
44+
--capabilities CAPABILITY_IAM \
45+
--parameter-overrides \
46+
CertificateArn=arn:aws:acm:us-east-1:123456789:certificate/your-cert-id
47+
```
48+
49+
### 2. Get the public URL
50+
51+
```bash
52+
aws cloudformation describe-stacks \
53+
--stack-name netlicensing-mcp \
54+
--query 'Stacks[0].Outputs' \
55+
--output table
56+
```
57+
58+
The `McpEndpointHttps` output is the URL to use in your MCP client config.
59+
60+
### 3. Verify
61+
62+
```bash
63+
curl https://your-alb-dns.us-east-1.elb.amazonaws.com/health
64+
# → {"status":"ok","server":"netlicensing-mcp"}
65+
```
66+
67+
### Architecture
68+
69+
```
70+
┌─────────────┐ HTTPS ┌────────────┐ HTTP ┌───────────────────┐
71+
│ MCP Client │ ─────────── → │ ALB │ ─────────── → │ Fargate Task │
72+
│ (Claude, │ │ (ACM TLS) │ │ netlicensing-mcp │
73+
│ Copilot) │ └────────────┘ │ :8000/mcp │
74+
└─────────────┘ └───────────────────┘
75+
```
76+
77+
---
78+
79+
## Option B — App Runner (simplest, scale-to-zero)
80+
81+
> **Note:** App Runner only supports ECR and ECR Public images — not GHCR
82+
> directly. Use the `mirror` command to copy the image first.
83+
84+
### 1. Mirror the image to ECR
85+
86+
```bash
87+
# Create an ECR repo (if it doesn't exist)
88+
aws ecr create-repository --repository-name netlicensing-mcp --region us-east-1
89+
90+
# Mirror using the helper script
91+
./deploy.sh mirror \
92+
--ecr-repo 123456789.dkr.ecr.us-east-1.amazonaws.com/netlicensing-mcp
93+
```
94+
95+
### 2. Deploy
96+
97+
```bash
98+
./deploy.sh apprunner \
99+
--ecr-image 123456789.dkr.ecr.us-east-1.amazonaws.com/netlicensing-mcp:latest
100+
```
101+
102+
### 3. Get the public URL
103+
104+
The stack output `McpEndpoint` contains the auto-provisioned HTTPS URL
105+
(e.g. `https://abc123.us-east-1.awsapprunner.com/mcp`).
106+
107+
---
108+
109+
## Connecting MCP Clients to the AWS Endpoint
110+
111+
Once deployed, configure your MCP client to use the public URL with the
112+
`streamable-http` transport:
113+
114+
### Claude Desktop / VS Code
115+
116+
To securely invoke the remote MCP server for a specific vendor account, pass your NetLicensing API Key using the `apikey` query parameter:
117+
118+
```json
119+
{
120+
"mcpServers": {
121+
"netlicensing": {
122+
"url": "https://your-alb-dns.us-east-1.elb.amazonaws.com/mcp?apikey=YOUR_API_KEY"
123+
}
124+
}
125+
}
126+
```
127+
128+
### Generic streamable-http client
129+
130+
Pass the API key dynamically per client via HTTP headers or in the URL string:
131+
132+
```python
133+
from mcp import ClientSession
134+
from mcp.client.streamable_http import streamablehttp_client
135+
136+
# You can pass the key in the URL: "https://your-endpoint/mcp?apikey=foo"
137+
# Or natively with headers:
138+
headers = {"X-NetLicensing-API-Key": "YOUR_API_KEY"}
139+
140+
async with streamablehttp_client("https://your-endpoint/mcp", headers=headers) as (read, write, _):
141+
async with ClientSession(read, write) as session:
142+
await session.initialize()
143+
tools = await session.list_tools()
144+
```
145+
146+
---
147+
148+
## Cleanup
149+
150+
```bash
151+
# Delete the stack and all resources
152+
./deploy.sh teardown --stack-name netlicensing-mcp
153+
154+
# Or directly:
155+
aws cloudformation delete-stack --stack-name netlicensing-mcp --region us-east-1
156+
```

deploy/aws/apprunner.yaml

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
AWSTemplateFormatVersion: "2010-09-09"
2+
Description: >
3+
NetLicensing MCP Server — AWS App Runner deployment.
4+
Simplest option: auto-provisions HTTPS, scales to zero, no VPC required.
5+
6+
# ═══════════════════════════════════════════════════════════════════════════════
7+
# PARAMETERS
8+
# ═══════════════════════════════════════════════════════════════════════════════
9+
10+
Parameters:
11+
ImageUri:
12+
Type: String
13+
Default: ghcr.io/labs64/netlicensing-mcp:latest
14+
Description: >
15+
Container image URI. For GHCR images you must first push to ECR
16+
(App Runner only supports ECR and ECR Public natively).
17+
18+
19+
Cpu:
20+
Type: String
21+
Default: "0.25 vCPU"
22+
AllowedValues: ["0.25 vCPU", "0.5 vCPU", "1 vCPU"]
23+
Description: Instance CPU
24+
25+
Memory:
26+
Type: String
27+
Default: "0.5 GB"
28+
AllowedValues: ["0.5 GB", "1 GB", "2 GB"]
29+
Description: Instance memory
30+
31+
MinSize:
32+
Type: Number
33+
Default: 0
34+
Description: >
35+
Minimum number of instances. Set to 0 for scale-to-zero
36+
(first request has ~5 s cold start).
37+
38+
MaxSize:
39+
Type: Number
40+
Default: 2
41+
Description: Maximum number of instances
42+
43+
McpVerbose:
44+
Type: String
45+
Default: "false"
46+
AllowedValues: ["true", "false"]
47+
Description: Enable verbose debug logging
48+
49+
# ═══════════════════════════════════════════════════════════════════════════════
50+
# RESOURCES
51+
# ═══════════════════════════════════════════════════════════════════════════════
52+
53+
Resources:
54+
55+
# ── ECR Access Role (required for pulling from ECR / ECR Public) ───────
56+
57+
AccessRole:
58+
Type: AWS::IAM::Role
59+
Properties:
60+
AssumeRolePolicyDocument:
61+
Version: "2012-10-17"
62+
Statement:
63+
- Effect: Allow
64+
Principal:
65+
Service: build.apprunner.amazonaws.com
66+
Action: sts:AssumeRole
67+
ManagedPolicyArns:
68+
- arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess
69+
70+
# ── App Runner Service ─────────────────────────────────────────────────
71+
72+
McpService:
73+
Type: AWS::AppRunner::Service
74+
Properties:
75+
ServiceName: netlicensing-mcp
76+
SourceConfiguration:
77+
AuthenticationConfiguration:
78+
AccessRoleArn: !GetAtt AccessRole.Arn
79+
AutoDeploymentsEnabled: false
80+
ImageRepository:
81+
ImageIdentifier: !Ref ImageUri
82+
ImageRepositoryType: ECR
83+
ImageConfiguration:
84+
Port: "8000"
85+
StartCommand: "http"
86+
RuntimeEnvironmentVariables:
87+
- Name: MCP_TRANSPORT
88+
Value: http
89+
- Name: MCP_HOST
90+
Value: "0.0.0.0"
91+
- Name: MCP_PORT
92+
Value: "8000"
93+
- Name: MCP_VERBOSE
94+
Value: !Ref McpVerbose
95+
InstanceConfiguration:
96+
Cpu: !Ref Cpu
97+
Memory: !Ref Memory
98+
AutoScalingConfigurationArn: !GetAtt AutoScaling.AutoScalingConfigurationArn
99+
HealthCheckConfiguration:
100+
Protocol: HTTP
101+
Path: /health
102+
Interval: 20
103+
Timeout: 5
104+
HealthyThreshold: 1
105+
UnhealthyThreshold: 3
106+
107+
# ── Auto-Scaling (supports scale-to-zero) ──────────────────────────────
108+
109+
AutoScaling:
110+
Type: AWS::AppRunner::AutoScalingConfiguration
111+
Properties:
112+
AutoScalingConfigurationName: !Sub "${AWS::StackName}-scaling"
113+
MinSize: !Ref MinSize
114+
MaxSize: !Ref MaxSize
115+
MaxConcurrency: 50
116+
117+
# ═══════════════════════════════════════════════════════════════════════════════
118+
# OUTPUTS
119+
# ═══════════════════════════════════════════════════════════════════════════════
120+
121+
Outputs:
122+
ServiceUrl:
123+
Description: Public HTTPS URL (auto-provisioned by App Runner)
124+
Value: !Sub "https://${McpService.ServiceUrl}"
125+
126+
McpEndpoint:
127+
Description: MCP streamable-http endpoint
128+
Value: !Sub "https://${McpService.ServiceUrl}/mcp"
129+
130+
HealthCheckUrl:
131+
Description: Health check URL
132+
Value: !Sub "https://${McpService.ServiceUrl}/health"
133+
134+
ServiceArn:
135+
Description: App Runner service ARN
136+
Value: !GetAtt McpService.ServiceArn
137+

0 commit comments

Comments
 (0)