pi-mcp-adapter 2.5.2 → 2.5.3

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 CHANGED
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.5.3] - 2026-05-01
11
+
12
+ ### Added
13
+ - Added environment variable and `~` expansion for stdio server `cwd` values.
14
+
10
15
  ## [2.5.2] - 2026-04-29
11
16
 
12
17
  ### Fixed
package/README.md CHANGED
@@ -120,9 +120,10 @@ Pi-specific files are the write targets for imported or shared global servers wh
120
120
  |-------|-------------|
121
121
  | `command` | Executable for stdio transport |
122
122
  | `args` | Command arguments |
123
- | `env` | Environment variables (`${VAR}` interpolation) |
124
- | `cwd` | Working directory |
123
+ | `env` | Environment variables; supports `${VAR}` and `$env:VAR` interpolation |
124
+ | `cwd` | Working directory; supports `${VAR}`, `$env:VAR`, and `~` expansion |
125
125
  | `url` | HTTP endpoint (StreamableHTTP with SSE fallback) |
126
+ | `headers` | HTTP headers; supports `${VAR}` and `$env:VAR` interpolation |
126
127
  | `auth` | `"bearer"` or `"oauth"` |
127
128
  | `oauth.grantType` | `"authorization_code"` (default) or `"client_credentials"` for non-interactive machine auth |
128
129
  | `bearerToken` / `bearerTokenEnv` | Token or env var name |
package/metadata-cache.ts CHANGED
@@ -7,7 +7,7 @@ import { getToolUiResourceUri } from "@modelcontextprotocol/ext-apps/app-bridge"
7
7
  import type { McpTool, McpResource, ServerEntry, ToolMetadata } from "./types.js";
8
8
  import { formatToolName, isToolExcluded } from "./types.js";
9
9
  import { resourceNameToToolName } from "./resource-tools.js";
10
- import { extractToolUiStreamMode } from "./utils.js";
10
+ import { extractToolUiStreamMode, interpolateEnvRecord, resolveConfigPath } from "./utils.js";
11
11
 
12
12
  const CACHE_VERSION = 1;
13
13
  const CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
@@ -88,12 +88,12 @@ export function computeServerHash(definition: ServerEntry): string {
88
88
  const identity: Record<string, unknown> = {
89
89
  command: definition.command,
90
90
  args: definition.args,
91
- env: definition.env,
92
- cwd: definition.cwd,
91
+ env: interpolateEnvRecord(definition.env),
92
+ cwd: resolveConfigPath(definition.cwd),
93
93
  url: definition.url,
94
- headers: definition.headers,
94
+ headers: interpolateEnvRecord(definition.headers),
95
95
  auth: definition.auth,
96
- bearerToken: definition.bearerToken,
96
+ bearerToken: definition.bearerToken ?? (definition.bearerTokenEnv ? process.env[definition.bearerTokenEnv] : undefined),
97
97
  bearerTokenEnv: definition.bearerTokenEnv,
98
98
  exposeResources: definition.exposeResources,
99
99
  excludeTools: definition.excludeTools,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mcp-adapter",
3
- "version": "2.5.2",
3
+ "version": "2.5.3",
4
4
  "description": "MCP (Model Context Protocol) adapter extension for Pi coding agent",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/server-manager.ts CHANGED
@@ -17,6 +17,7 @@ import { logger } from "./logger.js";
17
17
  import { McpOAuthProvider } from "./mcp-oauth-provider.js";
18
18
  import { supportsOAuth } from "./mcp-auth-flow.js";
19
19
  import { registerSamplingHandler, type ServerSamplingConfig } from "./sampling-handler.js";
20
+ import { interpolateEnvRecord, resolveConfigPath } from "./utils.js";
20
21
 
21
22
  interface ServerConnection {
22
23
  client: Client;
@@ -91,7 +92,7 @@ export class McpServerManager {
91
92
  command,
92
93
  args,
93
94
  env: resolveEnv(definition.env),
94
- cwd: definition.cwd,
95
+ cwd: resolveConfigPath(definition.cwd),
95
96
  stderr: definition.debug ? "inherit" : "ignore",
96
97
  });
97
98
  } else if (definition.url) {
@@ -362,28 +363,14 @@ function resolveEnv(env?: Record<string, string>): Record<string, string> {
362
363
  }
363
364
 
364
365
  if (!env) return resolved;
365
-
366
- for (const [key, value] of Object.entries(env)) {
367
- // Support ${VAR} and $env:VAR interpolation
368
- resolved[key] = value
369
- .replace(/\$\{(\w+)\}/g, (_, name) => process.env[name] ?? "")
370
- .replace(/\$env:(\w+)/g, (_, name) => process.env[name] ?? "");
371
- }
372
-
373
- return resolved;
366
+
367
+ const overrides = interpolateEnvRecord(env);
368
+ return overrides ? { ...resolved, ...overrides } : resolved;
374
369
  }
375
370
 
376
371
  /**
377
372
  * Resolve headers with environment variable interpolation.
378
373
  */
379
374
  function resolveHeaders(headers?: Record<string, string>): Record<string, string> | undefined {
380
- if (!headers) return undefined;
381
-
382
- const resolved: Record<string, string> = {};
383
- for (const [key, value] of Object.entries(headers)) {
384
- resolved[key] = value
385
- .replace(/\$\{(\w+)\}/g, (_, name) => process.env[name] ?? "")
386
- .replace(/\$env:(\w+)/g, (_, name) => process.env[name] ?? "");
387
- }
388
- return resolved;
375
+ return interpolateEnvRecord(headers);
389
376
  }
package/utils.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
- import { platform } from "node:os";
2
+ import { homedir, platform } from "node:os";
3
+ import { join } from "node:path";
3
4
  import type { McpConfig } from "./types.js";
4
5
 
5
6
  async function execOpen(pi: ExtensionAPI, target: string, browser?: string) {
@@ -58,6 +59,33 @@ export function getConfigPathFromArgv(): string | undefined {
58
59
  return undefined;
59
60
  }
60
61
 
62
+ export function interpolateEnvVars(value: string): string {
63
+ return value
64
+ .replace(/\$\{(\w+)\}/g, (_, name) => process.env[name] ?? "")
65
+ .replace(/\$env:(\w+)/g, (_, name) => process.env[name] ?? "");
66
+ }
67
+
68
+ export function interpolateEnvRecord(values: Record<string, string> | undefined): Record<string, string> | undefined {
69
+ if (!values) return undefined;
70
+
71
+ const resolved: Record<string, string> = {};
72
+ for (const [key, value] of Object.entries(values)) {
73
+ resolved[key] = interpolateEnvVars(value);
74
+ }
75
+ return resolved;
76
+ }
77
+
78
+ export function resolveConfigPath(value: string | undefined): string | undefined {
79
+ if (value === undefined) return undefined;
80
+
81
+ const resolved = interpolateEnvVars(value);
82
+ if (resolved === "~") return homedir();
83
+ if (resolved.startsWith("~/") || resolved.startsWith("~\\")) {
84
+ return join(homedir(), resolved.slice(2));
85
+ }
86
+ return resolved;
87
+ }
88
+
61
89
  export function truncateAtWord(text: string, target: number): string {
62
90
  if (!text || text.length <= target) return text;
63
91