pi-cloudflare 0.5.5 → 0.6.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/README.md +48 -20
- package/bin/setup.mjs +25 -9
- package/dist/index.d.ts +6 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -161
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +106 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/proxy.d.ts +7 -6
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +3 -2
- package/dist/proxy.js.map +1 -1
- package/dist/runtime.d.ts +52 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +186 -0
- package/dist/runtime.js.map +1 -0
- package/dist/token-store.d.ts +12 -7
- package/dist/token-store.d.ts.map +1 -1
- package/dist/token-store.js +18 -10
- package/dist/token-store.js.map +1 -1
- package/mcp.json +15 -0
- package/package.json +7 -3
- package/plugin.json +20 -0
- package/skills/cloudflare-auth-setup/SKILL.md +24 -18
- package/skills/cloudflare-token-roll/SKILL.md +7 -8
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# pi-cloudflare
|
|
2
2
|
|
|
3
|
-
One
|
|
4
|
-
helper
|
|
3
|
+
One package for Cloudflare-driven agent work: **skills + MCP tools + setup
|
|
4
|
+
helper**, distributed both as a native Pi package and an
|
|
5
|
+
[Agent Plugins 1.0](https://agent-plugins.org/) package.
|
|
5
6
|
|
|
6
7
|
- **Skills** (12 vendored from the official
|
|
7
8
|
[cloudflare/skills](https://github.com/cloudflare/skills) repo: `cloudflare`,
|
|
@@ -21,26 +22,45 @@ helper** in a single pi package.
|
|
|
21
22
|
| `cf_builds_*` | Workers Builds insights |
|
|
22
23
|
| `cf_obs_*` | Workers logs/metrics/traces |
|
|
23
24
|
|
|
24
|
-
Requires
|
|
25
|
-
|
|
26
|
-
tools work immediately after install.
|
|
25
|
+
Requires Node 20.19+ (or 22.12+) and network access. Native Pi installation
|
|
26
|
+
also requires Pi package support. No browser, wrangler CLI, or API token is
|
|
27
|
+
needed to install; the docs tools work immediately after install.
|
|
27
28
|
|
|
28
29
|

|
|
29
30
|
|
|
30
31
|
## Install
|
|
31
32
|
|
|
33
|
+
### Pi
|
|
34
|
+
|
|
32
35
|
```bash
|
|
33
36
|
pi install npm:pi-cloudflare
|
|
34
37
|
```
|
|
35
38
|
|
|
36
|
-
Tools and skills register on the **next** session start.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
Tools and skills register on the **next** session start.
|
|
40
|
+
|
|
41
|
+
### Agent Plugins 1.0
|
|
42
|
+
|
|
43
|
+
The published npm package is also a self-contained Agent Plugin. Its root
|
|
44
|
+
`plugin.json` discovers `skills/`, while `mcp.json` launches the portable
|
|
45
|
+
stdio gateway in `dist/mcp-server.js`.
|
|
46
|
+
|
|
47
|
+
Agent Plugins deliberately leaves installation sources to each client. Install
|
|
48
|
+
or unpack the npm package using the client-specific plugin flow, with the
|
|
49
|
+
package root as `PLUGIN_ROOT`. A raw Git checkout must be built first with
|
|
50
|
+
`npm ci && npm run build`; npm releases already contain `dist/`.
|
|
51
|
+
|
|
52
|
+
The portable gateway exposes the same `cf_api_*`, `cf_docs_*`,
|
|
53
|
+
`cf_bindings_*`, `cf_builds_*`, and `cf_obs_*` tool names as the native Pi
|
|
54
|
+
adapter. OAuth state is stored under the client-managed `${PLUGIN_DATA}`
|
|
55
|
+
directory rather than `~/.pi`.
|
|
56
|
+
|
|
57
|
+
(`web-perf` is intentionally not vendored: keep a local customized copy
|
|
58
|
+
instead, since the upstream version would clobber environment-specific rules.)
|
|
39
59
|
|
|
40
60
|
## Authenticate (one-time browser OAuth)
|
|
41
61
|
|
|
42
62
|
```bash
|
|
43
|
-
npx -p pi-cloudflare pi-cloudflare-setup
|
|
63
|
+
npx -p pi-cloudflare pi-cloudflare-setup # authorize missing servers
|
|
44
64
|
npx -p pi-cloudflare pi-cloudflare-setup --only builds # re-auth specific servers
|
|
45
65
|
```
|
|
46
66
|
|
|
@@ -50,9 +70,14 @@ you approve, and tokens (with refresh) are stored owner-only in
|
|
|
50
70
|
`~/.pi/cloudflare-tokens.json`. The extension refreshes them silently
|
|
51
71
|
before expiry, and setup skips servers that are still fresh. If a server
|
|
52
72
|
later rejects its token, rerun setup (with `--only` for just that one).
|
|
73
|
+
|
|
74
|
+
Agent Plugins clients use their own token file under `${PLUGIN_DATA}`. When
|
|
75
|
+
authentication is missing or rejected, call the generated
|
|
76
|
+
`cf_<server>_reauthenticate` tool and run the exact command it returns; that
|
|
77
|
+
command includes the plugin-local setup script and the correct data path.
|
|
53
78
|
Nothing is ever logged or committed.
|
|
54
79
|
|
|
55
|
-
## Configure (optional)
|
|
80
|
+
## Configure Pi (optional)
|
|
56
81
|
|
|
57
82
|
In `~/.pi/agent/settings.json`:
|
|
58
83
|
|
|
@@ -67,12 +92,11 @@ In `~/.pi/agent/settings.json`:
|
|
|
67
92
|
|
|
68
93
|
## Persistent auth (a month and beyond)
|
|
69
94
|
|
|
70
|
-
Browser OAuth access tokens live about an hour;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
tool appears with the same instructions.
|
|
95
|
+
Browser OAuth access tokens live about an hour; both adapters refresh them
|
|
96
|
+
silently before tool calls, so daily use normally does not re-authenticate.
|
|
97
|
+
When refresh itself is rejected (revoked, rotated away by a parallel login,
|
|
98
|
+
or expired), agents get an exact recovery command instead of a dead end — and
|
|
99
|
+
a `cf_<server>_reauthenticate` tool appears with the same instructions.
|
|
76
100
|
|
|
77
101
|
Two levers for longer-lived credentials:
|
|
78
102
|
|
|
@@ -83,11 +107,15 @@ Two levers for longer-lived credentials:
|
|
|
83
107
|
```jsonc
|
|
84
108
|
{
|
|
85
109
|
"pi-cloudflare": {
|
|
86
|
-
"apiToken": "${CLOUDFLARE_API_TOKEN}", //
|
|
110
|
+
"apiToken": "${CLOUDFLARE_API_TOKEN}", // native Pi
|
|
87
111
|
},
|
|
88
112
|
}
|
|
89
113
|
```
|
|
90
114
|
|
|
115
|
+
Agent Plugin hosts can supply `CLOUDFLARE_API_TOKEN` to the MCP subprocess
|
|
116
|
+
through their client-specific environment/secret mechanism. Agent Plugins
|
|
117
|
+
1.0 intentionally does not define a portable secret-reference field.
|
|
118
|
+
|
|
91
119
|
Create one at dash.cloudflare.com → Manage Account → API Tokens with
|
|
92
120
|
the scopes your agents need (exact template, additions, and account
|
|
93
121
|
scoping in `docs/api-token.md`). Other servers stay on browser OAuth.
|
|
@@ -124,9 +152,9 @@ closed on scope). They complement; neither replaces the other.
|
|
|
124
152
|
| Symptom | Likely cause | Fix |
|
|
125
153
|
| -------------------------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
126
154
|
| `Browser connection failed` on session start | A server is down or its token expired | The extension skips it with a warning and continues; rerun `pi-cloudflare-setup --only <server>` for the failing one |
|
|
127
|
-
| OAuth tab never opens | Headless environment or blocked popup | Copy the printed URL into any browser; tokens land in `~/.pi/cloudflare-tokens.json`
|
|
128
|
-
| `401` from one server only | That server's refresh token was revoked | `
|
|
129
|
-
| Tools missing for a server | Disabled
|
|
155
|
+
| OAuth tab never opens | Headless environment or blocked popup | Copy the printed URL into any browser; native Pi tokens land in `~/.pi/cloudflare-tokens.json` |
|
|
156
|
+
| `401` from one server only | That server's refresh token was revoked | Call `cf_<server>_reauthenticate` for the host-correct recovery command |
|
|
157
|
+
| Tools missing for a server | Disabled in native Pi settings | Re-enable it; tools register on next session start |
|
|
130
158
|
| Rate-limited API calls | Too many write calls in a loop | Back off and batch; prefer one `cf_api_execute` with a precise query over paginated scans |
|
|
131
159
|
|
|
132
160
|
## Development
|
package/bin/setup.mjs
CHANGED
|
@@ -2,20 +2,30 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* pi-cloudflare setup: one-time browser OAuth for every Cloudflare MCP
|
|
4
4
|
* server. Each server is its own OAuth issuer, so approval happens once per
|
|
5
|
-
* server (docs needs none).
|
|
6
|
-
*
|
|
5
|
+
* server (docs needs none).
|
|
6
|
+
*
|
|
7
|
+
* Native Pi defaults to ~/.pi/cloudflare-tokens.json. Agent Plugins clients
|
|
8
|
+
* pass --token-file with their ${PLUGIN_DATA} path so credentials remain
|
|
9
|
+
* client-local and do not require Pi to be installed.
|
|
7
10
|
*
|
|
8
11
|
* Usage:
|
|
9
|
-
* pi-cloudflare-setup
|
|
10
|
-
* pi-cloudflare-setup --only builds
|
|
12
|
+
* pi-cloudflare-setup
|
|
13
|
+
* pi-cloudflare-setup --only builds
|
|
11
14
|
* pi-cloudflare-setup --only api,observability
|
|
15
|
+
* pi-cloudflare-setup --token-file /path/to/cloudflare-tokens.json --only api
|
|
12
16
|
*
|
|
13
17
|
* Nothing secret is ever logged or written anywhere else.
|
|
14
18
|
*/
|
|
15
19
|
import { connectAll } from '../dist/client.js'
|
|
16
20
|
import { CLOUDFLARE_SERVERS } from '../dist/servers.js'
|
|
17
21
|
import { runOAuthFlow } from '../dist/auth-flow.js'
|
|
18
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
partitionServers,
|
|
24
|
+
readTokenFile,
|
|
25
|
+
readTokenFileAt,
|
|
26
|
+
writeTokenFile,
|
|
27
|
+
writeTokenFileAt,
|
|
28
|
+
} from '../dist/token-store.js'
|
|
19
29
|
|
|
20
30
|
const AUTHED_IDS = ['api', 'bindings', 'builds', 'observability']
|
|
21
31
|
|
|
@@ -39,9 +49,14 @@ function parseOnly() {
|
|
|
39
49
|
return ids
|
|
40
50
|
}
|
|
41
51
|
|
|
52
|
+
const tokenFile = argValue('--token-file')
|
|
53
|
+
const readStore = () => (tokenFile ? readTokenFileAt(tokenFile) : readTokenFile())
|
|
54
|
+
const writeStore = (servers) =>
|
|
55
|
+
tokenFile ? writeTokenFileAt(servers, tokenFile) : writeTokenFile(servers)
|
|
56
|
+
|
|
42
57
|
const only = parseOnly()
|
|
43
58
|
const wanted = only ?? AUTHED_IDS
|
|
44
|
-
const file =
|
|
59
|
+
const file = readStore() ?? { version: 1, servers: {} }
|
|
45
60
|
const { fresh, needed } = partitionServers(wanted, file.servers)
|
|
46
61
|
|
|
47
62
|
if (fresh.length > 0) {
|
|
@@ -62,7 +77,7 @@ if (needed.length === 0) {
|
|
|
62
77
|
try {
|
|
63
78
|
const { tokens, clientId, tokenEndpoint } = await runOAuthFlow(definition)
|
|
64
79
|
file.servers[id] = { ...tokens, clientId, tokenEndpoint }
|
|
65
|
-
|
|
80
|
+
writeStore(file.servers)
|
|
66
81
|
console.log(`[${index}/${needed.length}] ${id}: authorized and stored\n`)
|
|
67
82
|
} catch (error) {
|
|
68
83
|
console.log(
|
|
@@ -73,7 +88,7 @@ if (needed.length === 0) {
|
|
|
73
88
|
}
|
|
74
89
|
|
|
75
90
|
console.log('Verifying all servers...\n')
|
|
76
|
-
const stored =
|
|
91
|
+
const stored = readStore()
|
|
77
92
|
let okCount = 0
|
|
78
93
|
const failed = []
|
|
79
94
|
for (const definition of CLOUDFLARE_SERVERS) {
|
|
@@ -98,6 +113,7 @@ for (const definition of CLOUDFLARE_SERVERS) {
|
|
|
98
113
|
|
|
99
114
|
console.log(`\n${okCount}/5 servers working.`)
|
|
100
115
|
if (failed.length > 0) {
|
|
101
|
-
|
|
116
|
+
const tokenArg = tokenFile ? ` --token-file ${JSON.stringify(tokenFile)}` : ''
|
|
117
|
+
console.log(`Re-authenticate with: pi-cloudflare-setup${tokenArg} --only ${failed.join(',')}`)
|
|
102
118
|
process.exit(1)
|
|
103
119
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { type CloudflareServerDefinition } from './servers.js';
|
|
3
|
-
import { type StoredServerTokens } from './token-store.js';
|
|
1
|
+
export { isAuthFailure, reauthHint, syncFromFile } from './runtime.js';
|
|
4
2
|
interface PiToolDefinition {
|
|
5
3
|
name: string;
|
|
6
4
|
label?: string;
|
|
@@ -12,36 +10,13 @@ interface PiHost {
|
|
|
12
10
|
registerTool: (definition: PiToolDefinition) => void;
|
|
13
11
|
on: (event: 'session_start' | 'session_shutdown', handler: (...args: never[]) => unknown) => void;
|
|
14
12
|
}
|
|
15
|
-
interface ServerEntry {
|
|
16
|
-
definition: CloudflareServerDefinition;
|
|
17
|
-
connection?: ServerConnection;
|
|
18
|
-
stored?: StoredServerTokens;
|
|
19
|
-
/** Static bearer token (api server only): never expires, never refreshes. */
|
|
20
|
-
staticToken?: string;
|
|
21
|
-
timeoutMs: number;
|
|
22
|
-
/** Access token the live connection was built with; drift triggers reconnect. */
|
|
23
|
-
connectedToken?: string;
|
|
24
|
-
}
|
|
25
|
-
/** Exact command that re-authorizes one server; shown to agents, never guessed. */
|
|
26
|
-
export declare function reauthHint(serverId: string): string;
|
|
27
|
-
/** True when a failure smells like rejected credentials rather than a broken server. */
|
|
28
|
-
export declare function isAuthFailure(error: unknown): boolean;
|
|
29
|
-
/**
|
|
30
|
-
* Adopt rotations performed by sibling sessions: parallel Pi processes
|
|
31
|
-
* share one token file, and refresh grants are single-use. If the file
|
|
32
|
-
* holds a different grant than memory, the file wins without network I/O.
|
|
33
|
-
*/
|
|
34
|
-
export declare function syncFromFile(entry: ServerEntry, home?: string): void;
|
|
35
13
|
/**
|
|
36
|
-
* pi-coding-agent
|
|
14
|
+
* Native pi-coding-agent adapter.
|
|
37
15
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* prefix; unreachable servers are skipped with a re-auth hint instead of
|
|
43
|
-
* failing the session. On session_shutdown all connections close.
|
|
16
|
+
* The Cloudflare runtime is host-independent; Pi contributes only settings,
|
|
17
|
+
* lifecycle hooks, and tool registration. This preserves the existing
|
|
18
|
+
* `pi install npm:pi-cloudflare` behavior while Agent Plugins clients use the
|
|
19
|
+
* stdio adapter in mcp.json.
|
|
44
20
|
*/
|
|
45
21
|
export default function piCloudflareExtension(pi: PiHost): void;
|
|
46
|
-
export {};
|
|
47
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEtE,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,EAAE,CACP,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC,OAAO,CAAC,CAAA;CACtB;AAED,UAAU,MAAM;IACd,YAAY,EAAE,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,CAAA;IACpD,EAAE,EAAE,CAAC,KAAK,EAAE,eAAe,GAAG,kBAAkB,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,KAAK,IAAI,CAAA;CAClG;AAsCD;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAgB9D"}
|
package/dist/index.js
CHANGED
|
@@ -1,22 +1,8 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
1
2
|
import { homedir } from 'node:os';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import { connectServer } from './client.js';
|
|
6
|
-
import { refreshAccessToken } from './oauth.js';
|
|
7
|
-
import { resolveConfig } from './config.js';
|
|
8
|
-
import { buildAllRegistrations } from './proxy.js';
|
|
9
|
-
import { CLOUDFLARE_SERVERS } from './servers.js';
|
|
10
|
-
import { isExpired, readTokenFile, writeTokenFile } from './token-store.js';
|
|
11
|
-
async function reconnect(entry) {
|
|
12
|
-
await entry.connection?.close().catch(() => undefined);
|
|
13
|
-
const token = entry.staticToken ?? entry.stored?.accessToken;
|
|
14
|
-
entry.connection = await connectServer(entry.definition, {
|
|
15
|
-
apiToken: token,
|
|
16
|
-
timeoutMs: entry.timeoutMs,
|
|
17
|
-
});
|
|
18
|
-
entry.connectedToken = token;
|
|
19
|
-
}
|
|
4
|
+
import { CloudflareRuntime } from './runtime.js';
|
|
5
|
+
export { isAuthFailure, reauthHint, syncFromFile } from './runtime.js';
|
|
20
6
|
function expandEnvVars(value) {
|
|
21
7
|
if (typeof value === 'string') {
|
|
22
8
|
return value.replace(/\$\{([^}]+)\}/g, (_match, expr) => {
|
|
@@ -47,158 +33,27 @@ function loadUserConfig() {
|
|
|
47
33
|
function warn(message) {
|
|
48
34
|
console.warn(`[pi-cloudflare] ${message}`);
|
|
49
35
|
}
|
|
50
|
-
/** Exact command that re-authorizes one server; shown to agents, never guessed. */
|
|
51
|
-
export function reauthHint(serverId) {
|
|
52
|
-
return (`${serverId} needs re-authentication (token expired, revoked, or never granted). ` +
|
|
53
|
-
`For temporary access, run in a terminal (approve in the browser when it opens): ` +
|
|
54
|
-
`npx -p pi-cloudflare pi-cloudflare-setup --only ${serverId}. ` +
|
|
55
|
-
`For persistent access across sessions, create an API token instead — ` +
|
|
56
|
-
`see the cloudflare-auth-setup skill (it asks once, then sets up either path).`);
|
|
57
|
-
}
|
|
58
|
-
/** True when a failure smells like rejected credentials rather than a broken server. */
|
|
59
|
-
export function isAuthFailure(error) {
|
|
60
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
61
|
-
return /\b401\b|\b403\b|unauthorized|invalid_token|invalid_grant|token refresh failed|expired|forbidden|authenticate/i.test(message);
|
|
62
|
-
}
|
|
63
36
|
/**
|
|
64
|
-
*
|
|
65
|
-
* share one token file, and refresh grants are single-use. If the file
|
|
66
|
-
* holds a different grant than memory, the file wins without network I/O.
|
|
67
|
-
*/
|
|
68
|
-
export function syncFromFile(entry, home) {
|
|
69
|
-
const latest = readTokenFile(home)?.servers[entry.definition.id];
|
|
70
|
-
if (latest && latest.refreshToken !== entry.stored?.refreshToken) {
|
|
71
|
-
entry.stored = latest;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
/** Refresh unconditionally (caller decided the token is rejected). Persists and reconnects. */
|
|
75
|
-
async function refreshNow(entry) {
|
|
76
|
-
syncFromFile(entry);
|
|
77
|
-
const stored = entry.stored;
|
|
78
|
-
if (!stored?.refreshToken)
|
|
79
|
-
throw new Error(`${entry.definition.id}: no refresh token stored`);
|
|
80
|
-
const fresh = await refreshAccessToken({
|
|
81
|
-
tokenEndpoint: stored.tokenEndpoint,
|
|
82
|
-
clientId: stored.clientId,
|
|
83
|
-
refreshToken: stored.refreshToken,
|
|
84
|
-
});
|
|
85
|
-
entry.stored = { ...stored, ...fresh };
|
|
86
|
-
const file = readTokenFile() ?? { version: 1, servers: {} };
|
|
87
|
-
file.servers[entry.definition.id] = entry.stored;
|
|
88
|
-
writeTokenFile(file.servers);
|
|
89
|
-
await reconnect(entry);
|
|
90
|
-
}
|
|
91
|
-
/** Refresh an OAuth token that is expired (or nearly so), persisting the result. Static tokens skip refresh. */
|
|
92
|
-
async function ensureFreshToken(entry) {
|
|
93
|
-
if (entry.staticToken)
|
|
94
|
-
return entry.staticToken;
|
|
95
|
-
syncFromFile(entry);
|
|
96
|
-
const stored = entry.stored;
|
|
97
|
-
if (!stored)
|
|
98
|
-
return undefined;
|
|
99
|
-
if (!isExpired(stored) || !stored.refreshToken) {
|
|
100
|
-
if (!entry.connection || entry.connectedToken !== stored.accessToken) {
|
|
101
|
-
await reconnect(entry);
|
|
102
|
-
}
|
|
103
|
-
return stored.accessToken;
|
|
104
|
-
}
|
|
105
|
-
await refreshNow(entry);
|
|
106
|
-
return entry.stored?.accessToken;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* pi-coding-agent extension entry point.
|
|
37
|
+
* Native pi-coding-agent adapter.
|
|
110
38
|
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* prefix; unreachable servers are skipped with a re-auth hint instead of
|
|
116
|
-
* failing the session. On session_shutdown all connections close.
|
|
39
|
+
* The Cloudflare runtime is host-independent; Pi contributes only settings,
|
|
40
|
+
* lifecycle hooks, and tool registration. This preserves the existing
|
|
41
|
+
* `pi install npm:pi-cloudflare` behavior while Agent Plugins clients use the
|
|
42
|
+
* stdio adapter in mcp.json.
|
|
117
43
|
*/
|
|
118
44
|
export default function piCloudflareExtension(pi) {
|
|
119
|
-
let
|
|
45
|
+
let runtime;
|
|
120
46
|
pi.on('session_start', async () => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const config = resolveConfig(userConfig);
|
|
127
|
-
const file = readTokenFile();
|
|
128
|
-
const definitions = CLOUDFLARE_SERVERS.filter((server) => config.enabledServerIds.includes(server.id));
|
|
129
|
-
for (const definition of definitions) {
|
|
130
|
-
const entry = {
|
|
131
|
-
definition,
|
|
132
|
-
timeoutMs: config.connectTimeoutMs,
|
|
133
|
-
staticToken: definition.id === 'api' ? userConfig.apiToken : undefined,
|
|
134
|
-
};
|
|
135
|
-
try {
|
|
136
|
-
entry.stored = file?.servers[definition.id];
|
|
137
|
-
await ensureFreshToken(entry);
|
|
138
|
-
if (!entry.connection)
|
|
139
|
-
await reconnect(entry);
|
|
140
|
-
const tools = (await entry.connection?.listTools()) ?? [];
|
|
141
|
-
const registrations = buildAllRegistrations([
|
|
142
|
-
{
|
|
143
|
-
prefix: definition.prefix,
|
|
144
|
-
tools,
|
|
145
|
-
callTool: async (name, params) => {
|
|
146
|
-
if (!entry.connection)
|
|
147
|
-
throw new Error(`${definition.id}: not connected`);
|
|
148
|
-
return entry.connection.callTool(name, params);
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
]);
|
|
152
|
-
for (const registration of registrations) {
|
|
153
|
-
pi.registerTool({
|
|
154
|
-
...registration,
|
|
155
|
-
execute: async (toolCallId, params, signal) => {
|
|
156
|
-
await ensureFreshToken(entry);
|
|
157
|
-
try {
|
|
158
|
-
return await registration.execute(toolCallId, params, signal);
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
// Clock-fresh but server-rejected: one forced refresh plus
|
|
162
|
-
// reconnect and retry before giving the agent a dead end.
|
|
163
|
-
if (!isAuthFailure(error) || entry.staticToken || !entry.stored?.refreshToken) {
|
|
164
|
-
throw error;
|
|
165
|
-
}
|
|
166
|
-
try {
|
|
167
|
-
await refreshNow(entry);
|
|
168
|
-
return await registration.execute(toolCallId, params, signal);
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
172
|
-
throw new Error(`${definition.id}: request rejected (${detail}). ${reauthHint(definition.id)}`);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
},
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
entries.push(entry);
|
|
179
|
-
}
|
|
180
|
-
catch (error) {
|
|
181
|
-
warn(`${definition.id}: unavailable (${error instanceof Error ? error.message : String(error)}).`);
|
|
182
|
-
if (isAuthFailure(error)) {
|
|
183
|
-
// Agents rarely see process logs: register a stub they will find.
|
|
184
|
-
pi.registerTool({
|
|
185
|
-
name: `${definition.prefix}reauthenticate`,
|
|
186
|
-
label: `${definition.prefix}reauthenticate`,
|
|
187
|
-
description: `${definition.id} is not authenticated. Call this tool for exact re-authentication instructions.`,
|
|
188
|
-
parameters: Type.Object({}),
|
|
189
|
-
execute: async () => ({
|
|
190
|
-
content: [{ type: 'text', text: reauthHint(definition.id) }],
|
|
191
|
-
}),
|
|
192
|
-
});
|
|
193
|
-
warn(reauthHint(definition.id));
|
|
194
|
-
}
|
|
195
|
-
await entry.connection?.close().catch(() => undefined);
|
|
196
|
-
}
|
|
47
|
+
await runtime?.stop();
|
|
48
|
+
runtime = new CloudflareRuntime({ config: loadUserConfig() ?? {}, warn });
|
|
49
|
+
const registrations = await runtime.start();
|
|
50
|
+
for (const registration of registrations) {
|
|
51
|
+
pi.registerTool(registration);
|
|
197
52
|
}
|
|
198
53
|
});
|
|
199
54
|
pi.on('session_shutdown', async () => {
|
|
200
|
-
await
|
|
201
|
-
|
|
55
|
+
await runtime?.stop();
|
|
56
|
+
runtime = undefined;
|
|
202
57
|
});
|
|
203
58
|
}
|
|
204
59
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAGhC,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAEhD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAuBtE,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE;YAC9D,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACxC,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC7C,CAAC,CAAC,CAAA;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;IACzD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CACzE,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAAA;QAClF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAA;QAChD,MAAM,OAAO,GAAG,MAAM,CAAC,eAAe,CAAC,CAAA;QACvC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QAC7D,OAAO,aAAa,CAAC,OAAO,CAAuB,CAAA;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,SAAS,IAAI,CAAC,OAAe;IAC3B,OAAO,CAAC,IAAI,CAAC,mBAAmB,OAAO,EAAE,CAAC,CAAA;AAC5C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAAC,EAAU;IACtD,IAAI,OAAsC,CAAA;IAE1C,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE;QAChC,MAAM,OAAO,EAAE,IAAI,EAAE,CAAA;QACrB,OAAO,GAAG,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;QACzE,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;QAC3C,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;YACzC,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,EAAE,CAAC,kBAAkB,EAAE,KAAK,IAAI,EAAE;QACnC,MAAM,OAAO,EAAE,IAAI,EAAE,CAAA;QACrB,OAAO,GAAG,SAAS,CAAA;IACrB,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
4
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { CloudflareRuntime } from './runtime.js';
|
|
7
|
+
import { tokenFilePath } from './token-store.js';
|
|
8
|
+
function packageVersion() {
|
|
9
|
+
try {
|
|
10
|
+
const raw = readFileSync(new URL('../package.json', import.meta.url), 'utf8');
|
|
11
|
+
const parsed = JSON.parse(raw);
|
|
12
|
+
return typeof parsed.version === 'string' ? parsed.version : '0.0.0';
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return '0.0.0';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function positiveInteger(value) {
|
|
19
|
+
if (!value)
|
|
20
|
+
return undefined;
|
|
21
|
+
const parsed = Number(value);
|
|
22
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
|
|
23
|
+
}
|
|
24
|
+
function runtimeConfig() {
|
|
25
|
+
const timeout = positiveInteger(process.env.PI_CLOUDFLARE_CONNECT_TIMEOUT_MS);
|
|
26
|
+
return {
|
|
27
|
+
apiToken: process.env.CLOUDFLARE_API_TOKEN,
|
|
28
|
+
connectTimeoutMs: timeout,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function portableTokenFile() {
|
|
32
|
+
if (process.env.PI_CLOUDFLARE_TOKEN_FILE)
|
|
33
|
+
return process.env.PI_CLOUDFLARE_TOKEN_FILE;
|
|
34
|
+
if (process.env.PLUGIN_DATA)
|
|
35
|
+
return join(process.env.PLUGIN_DATA, 'cloudflare-tokens.json');
|
|
36
|
+
return tokenFilePath();
|
|
37
|
+
}
|
|
38
|
+
function portableSetupScript() {
|
|
39
|
+
const root = process.env.PI_CLOUDFLARE_PLUGIN_ROOT ?? process.env.PLUGIN_ROOT;
|
|
40
|
+
return root ? join(root, 'bin', 'setup.mjs') : undefined;
|
|
41
|
+
}
|
|
42
|
+
async function main() {
|
|
43
|
+
const runtime = new CloudflareRuntime({
|
|
44
|
+
config: runtimeConfig(),
|
|
45
|
+
tokenFile: portableTokenFile(),
|
|
46
|
+
setupScript: portableSetupScript(),
|
|
47
|
+
warn: (message) => console.error(`[pi-cloudflare] ${message}`),
|
|
48
|
+
});
|
|
49
|
+
let registrationsPromise;
|
|
50
|
+
const registrations = () => {
|
|
51
|
+
registrationsPromise ??= runtime.start();
|
|
52
|
+
return registrationsPromise;
|
|
53
|
+
};
|
|
54
|
+
const server = new Server({ name: 'pi-cloudflare', version: packageVersion() }, { capabilities: { tools: {} } });
|
|
55
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
56
|
+
const tools = await registrations();
|
|
57
|
+
return {
|
|
58
|
+
tools: tools.map((tool) => ({
|
|
59
|
+
name: tool.name,
|
|
60
|
+
description: tool.description,
|
|
61
|
+
inputSchema: tool.inputSchema,
|
|
62
|
+
})),
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
66
|
+
const tools = await registrations();
|
|
67
|
+
const tool = tools.find((candidate) => candidate.name === request.params.name);
|
|
68
|
+
if (!tool) {
|
|
69
|
+
return {
|
|
70
|
+
content: [{ type: 'text', text: `Unknown tool: ${request.params.name}` }],
|
|
71
|
+
isError: true,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return await tool.execute('agent-plugin', request.params.arguments ?? {}, undefined);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
return {
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: 'text',
|
|
82
|
+
text: error instanceof Error ? error.message : String(error),
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
isError: true,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
const transport = new StdioServerTransport();
|
|
90
|
+
await server.connect(transport);
|
|
91
|
+
const shutdown = async () => {
|
|
92
|
+
await runtime.stop();
|
|
93
|
+
await server.close();
|
|
94
|
+
};
|
|
95
|
+
process.once('SIGINT', () => {
|
|
96
|
+
void shutdown().finally(() => process.exit(0));
|
|
97
|
+
});
|
|
98
|
+
process.once('SIGTERM', () => {
|
|
99
|
+
void shutdown().finally(() => process.exit(0));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
main().catch((error) => {
|
|
103
|
+
console.error(`[pi-cloudflare] MCP server failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
104
|
+
process.exitCode = 1;
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=mcp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
|
package/dist/proxy.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
1
2
|
import type { ServerConnection } from './client.js';
|
|
2
3
|
export interface ToolRegistration {
|
|
3
4
|
name: string;
|
|
4
5
|
label: string;
|
|
5
6
|
description: string;
|
|
7
|
+
/** Raw JSON Schema for MCP clients. */
|
|
8
|
+
inputSchema: Record<string, unknown>;
|
|
9
|
+
/** TypeBox view of inputSchema for the native Pi host. */
|
|
6
10
|
parameters: unknown;
|
|
7
|
-
execute: (toolCallId: string, params: Record<string, unknown>, signal
|
|
8
|
-
content: unknown[];
|
|
9
|
-
isError?: boolean;
|
|
10
|
-
}>;
|
|
11
|
+
execute: (toolCallId: string, params: Record<string, unknown>, signal?: AbortSignal) => Promise<CallToolResult>;
|
|
11
12
|
}
|
|
12
13
|
interface UpstreamTool {
|
|
13
14
|
name: string;
|
|
@@ -15,8 +16,8 @@ interface UpstreamTool {
|
|
|
15
16
|
inputSchema: Record<string, unknown>;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
|
-
* Build
|
|
19
|
-
*
|
|
19
|
+
* Build host-neutral tool registrations for one upstream server. The raw
|
|
20
|
+
* schema is retained for MCP while Pi receives the TypeBox representation.
|
|
20
21
|
*/
|
|
21
22
|
export declare function buildToolRegistrations(prefix: string, tools: UpstreamTool[], callUpstream: (name: string, params: Record<string, unknown>) => Promise<unknown>): ToolRegistration[];
|
|
22
23
|
/** Prefix every tool of every connected server; names stay unique per server. */
|