@postqode/mcp
v0.9.0
Published
Host-agnostic Model Context Protocol client for PostQode: hub, OAuth manager, typed Zod schemas, shared server/tool/resource types.
Downloads
1,366
Readme
@postqode/mcp
Host-agnostic Model Context Protocol client layer. Covers everything from typed Zod schemas and shared server/tool/resource types up through the full connection orchestrator (McpHub) and OAuth flow (McpOAuthManager).
The extension's VS Code adapter lives in src/core/controller/index.ts. Any other consumer — CLI, standalone runtime, another agent harness — can supply its own callbacks for UI notifications, webview state pushes, telemetry, callback URLs, browser opening, and secret storage.
What ships
- Constants & schemas —
DEFAULT_MCP_TIMEOUT_SECONDS,POSTQODE_MCP_TOOL_IDENTIFIER,BaseConfigSchema,ServerConfigSchema,McpSettingsSchema(Zod). - Shared types —
McpServer,McpTool,McpResource,McpResourceTemplate,McpOAuthAuthStatus,McpMode,McpConnection,Transport,McpServerConfig. McpHub— watches the settings file, connects to each configured server over stdio / SSE / streamable-HTTP, and dispatchestools/call/resources/readrequests. Handles timeout config, auto-approve, reconnects on UnauthorizedError, and tool-result notifications.McpOAuthManager— per-serverOAuthClientProviderwith PKCE state, token refresh, pending-URL buffering so the browser only opens when the user clicks "Authenticate".getServerAuthHash,getMcpServerCallbackPath— the two stable helpers shared by callback-URL routing and secret keying.
Minimal usage
import { McpHub, type McpHubConfig } from "@postqode/mcp"
import * as os from "node:os"
import * as path from "node:path"
import * as fs from "node:fs/promises"
// Pluggable secret store — use a dotfile here; the extension wires
// VS Code's secretStorage instead.
const secretsFile = path.join(os.homedir(), ".postqode", "mcp-secrets.json")
const authTokenStore = {
get: async (key: string) => {
const raw = await fs.readFile(secretsFile, "utf8").catch(() => "{}")
return (JSON.parse(raw) as Record<string, string>)[key]
},
set: async (key: string, value: string) => {
const raw = await fs.readFile(secretsFile, "utf8").catch(() => "{}")
const obj = JSON.parse(raw) as Record<string, string>
obj[key] = value
await fs.writeFile(secretsFile, JSON.stringify(obj))
},
}
const config: McpHubConfig = {
getMcpServersPath: async () => path.join(os.homedir(), ".postqode", "mcp"),
getSettingsDirectoryPath: async () => path.join(os.homedir(), ".postqode"),
clientVersion: "my-cli/0.1.0",
oauth: {
getCallbackUrl: async () => "http://localhost:3000",
openExternalUrl: async (url) => {
console.log("Open this URL to authenticate:", url)
},
authTokenStore,
},
host: {
showMessage: ({ type, message }) => console[type === "error" ? "error" : "log"](message),
onServersUpdated: (servers) => console.log(`[mcp] ${servers.length} servers connected`),
// captureToolCall is optional — omit if you don't need telemetry.
},
}
const hub = new McpHub(config)
// hub.getServers() → read current server list
// hub.callTool(serverName, toolName, args) → invoke a toolCallback surface
Required callbacks — no sane SDK defaults exist.
| Field | Purpose |
|---|---|
| oauth.getCallbackUrl() | Base URL the OAuth provider redirects to. |
| oauth.openExternalUrl(url) | Opens the URL in the user's browser. |
| oauth.authTokenStore.{get,set} | Pluggable secret storage for tokens + PKCE. |
Optional host callbacks — default to no-ops.
| Field | Default | Purpose |
|---|---|---|
| host.showMessage | no-op | Surface a user-visible notification (info / warning / error). |
| host.onServersUpdated | no-op | Called on every server-list change. The extension pushes to its webview. |
| host.captureToolCall | no-op | Lifecycle event for telemetry (started / success / error). |
| mcpSettingsFileName | "postqode_mcp_settings.json" | Basename of the settings file. |
Design note
The 1,700 LOC orchestration layer is genuinely reusable — the coupling to HostProvider, StateManager, PostHog, and the webview RPC was the only reason it used to live in src/services/mcp/. Every site is now a callback; the extension's adapter is ~35 lines.
Sibling packages
@postqode/agent— agent loop that calls MCP tools viaTooladapters.@postqode/coding-agent— harness that can layer MCP tools on top of its default pack.
