@edgegap/mcp 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -1,15 +1,40 @@
1
1
  # edgegap-mcp
2
2
 
3
- An MCP server that lets a coding agent take a developer from "I have a game
4
- server container" to "players are connected to it" without the developer
5
- reading the API reference.
3
+ An MCP server for Edgegap that lets a coding agent take a developer from "I
4
+ have a game server container" to "players are connected to it" without the
5
+ developer reading the API reference.
6
6
 
7
7
  Ten tools, hand-picked. Not generated from the OpenAPI spec — see
8
8
  [Scope](#scope) for why.
9
9
 
10
10
  ## Install
11
11
 
12
- One line in your MCP client config. Nothing to clone, nothing to build.
12
+ Two ways to run it. Pick based on how much you care about where your token
13
+ goes — see [Where your token goes](#where-your-token-goes).
14
+
15
+ ### Remote endpoint
16
+
17
+ Hosted by Edgegap as a Cloudflare Worker. Nothing to install.
18
+
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "edgegap": {
23
+ "type": "http",
24
+ "url": "https://mcp.edgegap.dev/mcp",
25
+ "headers": { "Authorization": "token YOUR_API_TOKEN" }
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ Also works as a custom connector in claude.ai: add
32
+ `https://mcp.edgegap.dev/mcp` and supply the same token.
33
+
34
+ ### Local
35
+
36
+ Runs on your own machine, spawned by your editor. One line in your MCP client
37
+ config, nothing to clone, nothing to build.
13
38
 
14
39
  ```json
15
40
  {
@@ -25,15 +50,18 @@ One line in your MCP client config. Nothing to clone, nothing to build.
25
50
  Works in Claude Code, Cursor, Codex, and VS Code. Pin a version in production
26
51
  (`@edgegap/mcp@0.1.0`) rather than floating on latest.
27
52
 
28
- > **Node version:** the server itself needs Node 18+. Deploying the optional
29
- > Cloudflare Worker needs Node 22+, because `wrangler` requires it.
53
+ Registered in the official MCP registry as `dev.edgegap/mcp`.
54
+
55
+ > **Node version:** the local server needs Node 18+. Deploying your own copy of
56
+ > the Cloudflare Worker needs Node 22+, because `wrangler` requires it.
57
+
58
+ ## Where your token goes
30
59
 
31
- ## Your token never leaves your machine
60
+ This differs by mode, and the difference is the reason both modes exist.
32
61
 
33
- There is no Edgegap-hosted component. This server runs as a process on your
34
- own computer, spawned by your editor. The first tool call asks you for a token,
35
- shows what it authorises, and requires an explicit acknowledgement before
36
- accepting it. Where that token then lives, exhaustively:
62
+ **Local.** The server runs as a process on your own computer. The first tool
63
+ call asks you for a token, shows what it authorises, and requires an explicit
64
+ acknowledgement before accepting it. Where that token then lives, exhaustively:
37
65
 
38
66
  - one variable in that process's memory, for the life of your editor session
39
67
 
@@ -42,18 +70,24 @@ any Edgegap server — the only thing sent to Edgegap is the API call itself,
42
70
  exactly as if you had run `curl`. Closing your editor revokes this server's
43
71
  access completely.
44
72
 
73
+ **Remote.** Your token is sent to `mcp.edgegap.dev` on every request and
74
+ forwarded from there to the Edgegap API. It transits infrastructure Edgegap
75
+ operates. The worker holds it for the life of the request and does not persist
76
+ it, but that is a "we don't store it" claim rather than a "we never see it"
77
+ claim. The two are different, and only local mode makes the second one.
78
+
45
79
  Generate a token at <https://app.edgegap.com/user-settings?tab=tokens>.
46
80
 
47
- Setting `EDGEGAP_API_TOKEN` still works and takes precedence, for CI and for
48
- clients that cannot show prompts. Do not pass a token as a command-line
49
- argument — arguments are visible to other processes via `ps`, and the server
50
- warns if it detects one.
81
+ In local mode, setting `EDGEGAP_API_TOKEN` takes precedence over the prompt,
82
+ for CI and for clients that cannot show prompts. Do not pass a token as a
83
+ command-line argument — arguments are visible to other processes via `ps`, and
84
+ the server warns if it detects one.
51
85
 
52
- **Why this is not hosted.** A hosted server would have to either store your
53
- token or receive it on every request. "We don't store it" and "we never see it"
54
- are different claims, and only a local process makes the second one. See
55
- `worker/DECISION.md` for the full reasoning and the conditions under which a
56
- hosted version becomes worth building.
86
+ **Which to use.** Remote for a first try, a demo, or a supervised session where
87
+ setup friction matters more than custody. Local for anything unattended,
88
+ anything in an organization with a live game in it, and anything where you
89
+ would rather not extend trust you don't have to. The guardrails described below
90
+ exist only in local mode.
57
91
 
58
92
  ## Read this before connecting an agent
59
93
 
@@ -70,22 +104,28 @@ Consequences worth being deliberate about:
70
104
  - Anything the agent logs, echoes, or sends to a model provider is a place the
71
105
  token could end up. This server does not log it, but it cannot control what
72
106
  the rest of the agent does.
107
+ - On the remote endpoint, the same unscoped token is additionally handled by
108
+ Edgegap's worker on every call.
73
109
 
74
110
  Recommended setup, in decreasing order of caution:
75
111
 
76
112
  | Situation | Setup |
77
113
  | --- | --- |
78
- | Unattended or autonomous agent | Separate non-production organization, plus `EDGEGAP_READ_ONLY=1` |
79
- | Supervised agent, live game in the org | `EDGEGAP_APP_ALLOWLIST` scoped to the app being worked on, plus `EDGEGAP_MAX_DURATION_MINUTES` |
80
- | Solo developer, no production workload | Defaults are fine; revoke the token when finished |
114
+ | Unattended or autonomous agent | Local mode. Separate non-production organization, plus `EDGEGAP_READ_ONLY=1` |
115
+ | Supervised agent, live game in the org | Local mode. `EDGEGAP_APP_ALLOWLIST` scoped to the app being worked on, plus `EDGEGAP_MAX_DURATION_MINUTES` |
116
+ | Solo developer, no production workload | Either mode. Defaults are fine; revoke the token when finished |
81
117
 
82
- The allowlist and read-only flag are enforced in this server, which means they
83
- protect against an agent that makes a mistake, not against one that has been
84
- compromised into calling the API directly. They narrow the blast radius; they
85
- do not remove it.
118
+ The allowlist and read-only flag are enforced in the local server, which means
119
+ they protect against an agent that makes a mistake, not against one that has
120
+ been compromised into calling the API directly. They narrow the blast radius;
121
+ they do not remove it.
86
122
 
87
123
  ### Environment variables
88
124
 
125
+ These configure the local server. On the remote endpoint they are set by
126
+ Edgegap and cannot be changed per developer — if you need any of them, run
127
+ locally.
128
+
89
129
  | Variable | Default | Purpose |
90
130
  | --- | --- | --- |
91
131
  | `EDGEGAP_API_TOKEN` | *(prompted)* | API token. Optional — omit it and the developer is asked at first use. The `token ` prefix is added for you. |
@@ -94,22 +134,23 @@ do not remove it.
94
134
  | `EDGEGAP_MAX_DURATION_MINUTES` | `60` | Ceiling on `max_duration` the agent may set on a version. Caps runaway cost from an unattended agent. |
95
135
  | `EDGEGAP_TIMEOUT_MS` | `30000` | Per-request HTTP timeout. |
96
136
 
97
- ## The ten tools
137
+ ## Tools
98
138
 
99
- Ordered along the golden path.
139
+ Ten tools, listed in the order they fall along the golden path. The same ten in
140
+ both modes.
100
141
 
101
- | # | Tool | Mutating | What it's for |
102
- | --- | --- | --- | --- |
103
- | 1 | `edgegap_list_apps` | | Orient before doing anything. Prevents duplicate applications. |
104
- | 2 | `edgegap_create_app` | ● | Create the container for versions. |
105
- | 3 | `edgegap_list_app_versions` | | Find a deployable version, or copy settings from a working one. |
106
- | 4 | `edgegap_create_app_version` | ● | Register a container image with CPU, memory, and ports. |
107
- | 5 | `edgegap_deploy` | ● | Start one instance near specified players. |
108
- | 6 | `edgegap_get_deployment` | | Single status read. |
109
- | 7 | `edgegap_wait_for_deployment` | | Poll to ready with backoff, then return the connection address. |
110
- | 8 | `edgegap_list_deployments` | | Find orphaned servers from earlier sessions. |
111
- | 9 | `edgegap_stop_deployment` | ● | Graceful SIGTERM, one deployment at a time. |
112
- | 10 | `edgegap_get_deployment_logs` | | Container output and crash exit code after a failure. |
142
+ | Tool | Mutating | What it's for |
143
+ | --- | --- | --- |
144
+ | `edgegap_list_apps` | | Orient before doing anything. Prevents duplicate applications. |
145
+ | `edgegap_create_app` | ● | Create the container for versions. |
146
+ | `edgegap_list_app_versions` | | Find a deployable version, or copy settings from a working one. |
147
+ | `edgegap_create_app_version` | ● | Register a container image with CPU, memory, and ports. |
148
+ | `edgegap_deploy` | ● | Start one instance near specified players. |
149
+ | `edgegap_get_deployment` | | Single status read. |
150
+ | `edgegap_wait_for_deployment` | | Poll to ready with backoff, then return the connection address. |
151
+ | `edgegap_list_deployments` | | Find orphaned servers from earlier sessions. |
152
+ | `edgegap_stop_deployment` | ● | Graceful SIGTERM, one deployment at a time. |
153
+ | `edgegap_get_deployment_logs` | | Container output and crash exit code after a failure. |
113
154
 
114
155
  ## Design decisions
115
156
 
@@ -135,6 +176,13 @@ player location are caught here rather than surfacing as an opaque 400.
135
176
  There is no bulk-stop tool, because an agent with a filter expression and a bug
136
177
  can stop a production fleet.
137
178
 
179
+ **Both a hosted endpoint and a local package.** The hosted endpoint removes
180
+ every step between finding this server and calling a tool, which is where most
181
+ developers drop out. The local package is the only way to run the server
182
+ without extending custody of an unscoped token to a third party, including us.
183
+ Neither one dominates the other, so both ship. See `worker/DECISION.md` for the
184
+ longer version.
185
+
138
186
  ## Scope
139
187
 
140
188
  Not exposed, on purpose: matchmaking, relays, private fleets, smart fleets,
@@ -147,6 +195,9 @@ would trade the conversion path for surface area.
147
195
 
148
196
  ## Known limitation: asking for the token at all
149
197
 
198
+ This applies to local mode, where the token is collected through elicitation
199
+ rather than read from config.
200
+
150
201
  The MCP specification says servers should not use elicitation to collect
151
202
  sensitive data, and an API token is sensitive. This server does it anyway,
152
203
  because requiring a token in a config file before anything works is the largest
@@ -159,9 +210,10 @@ plain-language disclosure, required acknowledgement, redaction from all output,
159
210
  and the environment variable always winning when present. Removing any of them
160
211
  breaks the trade.
161
212
 
162
- The real fix is on Edgegap's side: scoped, revocable, deploy-only credentials,
163
- issued through OAuth rather than pasted as a secret. Until those exist, the
164
- interactive prompt is a workaround and is labelled as one in the code.
213
+ The real fix is on Edgegap's side and would improve both modes: scoped,
214
+ revocable, deploy-only credentials, issued through OAuth rather than pasted as
215
+ a secret. Until those exist, the interactive prompt is a workaround and is
216
+ labelled as one in the code.
165
217
 
166
218
  ## Development
167
219
 
package/dist/auth.js CHANGED
@@ -189,24 +189,30 @@ export class TokenProvider {
189
189
  return token;
190
190
  }
191
191
  }
192
- /**
193
- * Credential source for the hosted Worker: the token arrives on the request
194
- * and lives only as long as this object, which is created per request and
195
- * garbage collected with it. Nothing is cached across requests, deliberately.
196
- */
197
192
  export class StaticTokenProvider {
198
193
  token;
194
+ unavailableMessage;
199
195
  used = false;
200
- constructor(token) {
196
+ /**
197
+ * @param token credential for this request, if the caller found a usable one
198
+ * @param unavailableMessage what to tell the agent when it calls a tool and
199
+ * there is no token. The transport knows WHY the token is missing — absent
200
+ * header, or a header carrying some other client's credential — and that
201
+ * distinction is the whole difference between a developer who can fix their
202
+ * setup and one staring at a generic 401. Defaults to the generic text.
203
+ */
204
+ constructor(token, unavailableMessage) {
201
205
  this.token = token;
206
+ this.unavailableMessage = unavailableMessage;
202
207
  }
203
208
  get current() {
204
209
  return this.token;
205
210
  }
206
211
  async get() {
207
212
  if (!this.token) {
208
- throw new TokenUnavailableError('No Edgegap API token on this request. Send it as an Authorization ' +
209
- 'header on the MCP connection.');
213
+ throw new TokenUnavailableError(this.unavailableMessage ??
214
+ 'No Edgegap API token on this request. Send it as an Authorization ' +
215
+ 'header on the MCP connection.');
210
216
  }
211
217
  return this.token;
212
218
  }
package/dist/tools.js CHANGED
@@ -55,7 +55,13 @@ function guard(auth, fn) {
55
55
  function errorHint(err) {
56
56
  switch (err.status) {
57
57
  case 401:
58
- return 'the token was rejected. It has been discarded; the next call will ask for a new one.';
58
+ // Deliberately does not promise a re-prompt: the local server asks again
59
+ // on the next call, the hosted relay cannot, and a hint that lies about
60
+ // what happens next sends the developer looking in the wrong place.
61
+ return ('Edgegap rejected the token. It may be expired, revoked, or from a ' +
62
+ 'different organization. Check it at ' +
63
+ 'https://app.edgegap.com/user-settings?tab=tokens. The token has been ' +
64
+ 'discarded from this session.');
59
65
  case 404:
60
66
  return 'the application or version name does not exist. Call edgegap_list_apps first.';
61
67
  case 409:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@edgegap/mcp",
3
3
  "mcpName": "dev.edgegap/mcp",
4
- "version": "0.1.2",
4
+ "version": "0.1.4",
5
5
  "description": "Deploy game servers on Edgegap from your coding agent. Runs locally; your API token never leaves your machine.",
6
6
  "scripts": {
7
7
  "prebuild": "rm -rf dist",