Blog
Thoughts, tutorials, and deep dives from the pyRPC team.
Getting Started With MCP: A Practical Guide
Comprehensive walkthrough for both MCP surfaces. Remote documentation in 30 seconds, local project in 15 minutes, both together in production.
The Future of AI Development Tools
Protocol convergence, registry servers, agent-first design, trust infrastructure. Where AI-native development is heading and what it means for Python frameworks.
Building Trust With AI Agents: The pyRPC Approach
Never-execute guarantees, dry-run-first defaults, structured errors, transparent discovery. How pyRPC design builds trust between developers and agents.
Why Local MCP Beats API Docs for AI Agents
Static documentation drifts. MCP imports your actual code. Ground truth vs interpretation, and why structured data beats text for agent performance.
AI-Native Development: What We Learned Building pyRPC MCP
Trust boundaries, read-only-first design, thin servers, protocol convergence. Lessons from building an AI-native Python framework from scratch.
How to Set Up MCP in Under 5 Minutes
Quickstart for both remote and local MCP. One command for documentation, five minutes for your project. The it-just-works principle.
The Catch With Remote MCP Servers
Claude Desktop refuses them. The auth gap in MCP spec. Rate limits. An honest discussion of remote MCP tradeoffs and why the two-server pattern still works.
How to Add MCP to Any Python Framework
Step-by-step guide to adding pyRPC local MCP to FastAPI, Django, Flask, or any ASGI/WSGI app. Framework-specific gotchas included.
The Protocol Is the Interface
Why MCP is the right abstraction layer for AI-tool integration. JSON-RPC foundations, Streamable HTTP vs stdio, and the thin server principle.
When an Agent Breaks Your Schema
AI agents generate plausible but type-incorrect payloads. How pyRPC check_call validates against real Python types and returns per-parameter errors the agent can fix.
Testing the MCP Like Prisma Does
Official SDK in-memory clients, raw subprocess JSON-RPC, and the purity test. A 21-test strategy that catches protocol bugs, lifecycle issues, and config regressions.
MCP Without a Launcher: The npx @pyrpc/mcp mcp Pattern
How a 50-line branded wrapper over add-mcp configures 19 AI coding agents without maintaining a single line of protocol or client-specific code.
Local vs Remote: The Trust Boundary That Matters
Two MCP servers, two trust models. Why the distinction between a subprocess importing your code and a hosted documentation endpoint is the most important design decision.
Three Tools, No More
introspect_project, check_call, run_codegen. How pyRPC chose exactly three tools and why the anti-pattern of too many tools kills agent performance.
Why MCP Is an Extra, Not a Default
Eighteen transitive packages including compiled cryptography. The packaging math behind making pyrpc-core[mcp] optional, and why that boundary is permanent.
stdout belongs to the protocol: stdio discipline in pyrpc mcp
One stray print corrupts an MCP session. How the SDK protects the wire, the two windows it cannot cover, and the tests that make purity mechanical.
Errors an agent can fix: the ToolError discipline
Structured failures with remediation baked in: what failed, what state caused it, and the exact next move, for every failure mode the server can hit.
Codegen through an MCP: dry run first, narrow writes, honest hints
The one mutating tool in pyrpc mcp gets a non-mutating default, generated-files-only scope, and annotations that match reality.
check_call: validation with execution removed from the universe
Bind-and-validate extracted from Procedure.execute so agents can verify payloads against real types while remaining structurally unable to run your code.
Introspection that imports your code, on purpose
Static parsers guess; the runtime knows. How the MCP reuses the registry chain from pyrpc.json to get_registry_schema for ground truth.
pyRPC v0.14.0: pyrpc mcp, your backend as an AI-native surface
Release tour: the local MCP server with three read-oriented tools, the no-execution security line, dry-run-first codegen, and lean optional packaging.
pyRPC v0.14.1: Batch requests now work in FastAPI
Patch release: the FastAPI adapter now accepts batch requests. What was broken, why other adapters were fine, and how to upgrade.
When your framework validates before your library does
The FastAPI batch bug as a case study in double validation — why framework annotations can block library features, and how a one-line type fix resolves it.
Batch requests in pyRPC: how they work and when to use them
A guide to batch RPC operations — what they look like on the wire, how the server processes them, and when httpBatchLink beats httpLink.
Dependabot across two lockfile ecosystems
Four surfaces (uv.lock, npm workspaces, docs npm, GitHub Actions), one policy: grouped minor+patch to cut noise, majors kept individual because a major pydantic bump is an event.
Docs and examples as executable truth
Twelve example apps and dozens of doc snippets were never executed by anything. Now every example imports cleanly in CI, every python fence parses, and fences tagged test run for real.
Coverage floors without theater
Measure first, pick the number second: a hard 78 percent floor against a measured 80, two points of slack absorbing noise instead of inviting gamed tests, and honesty about what line coverage cannot say.
The Windows CI leg found a real bug on day one
Expanding the matrix to Windows immediately surfaced a first-run crash: jsonc-edit bootstrapped npm via CreateProcess, which cannot execute npm.cmd. Fixed upstream in 0.2.1; floor raised so users get it.
Type checking a library whose product is types
mypy over five Python packages found thirteen errors, including an ASGI transport whose annotation still claimed dict-only payloads after batching shipped. Plus typed lazy globals and a Popen subclass replacing proc._cwd.
Adopting ruff into a codebase with 374 violations
The playbook: autofix the boring parts, hand-fix what teaches something (three missing raise-from chains, a closure bug), encode accepted style as documented config, then let CI hold the line.
From paper standards to enforced gates
Every engineering standard pyRPC claimed already existed in the repo; none were enforced. The audit, the rule set for closing seven gaps honestly, and what enforcement caught in its first days.
pyRPC v0.13.0: explicit backends and native dev servers
Release tour: declared backend config, framework-native dev servers, terminating links with batching, jailed autocomplete, and the site redesign - plus the two-step upgrade path.
Twelve examples, one conformance bar
Links-based clients everywhere, fixed provider scope in CRA, working Svelte inputs, Vue-idiomatic hooks, restored configs - what conformance means for an examples matrix that must stay green.
Restructuring the docs for adapters, links, and AI
Why client adapters got their own section, links earned dedicated pages, llms.txt and MCP became first-class navigation, and small fixes like restored sidebar icons compound.
Redesigning the site around the code
New brand assets, better-auth-style light/dark tokens, fumadocs 16.12 migration, and one shared Shiki palette so landing, docs, and playground render code identically.
Django gets first-class treatment in v0.13.0
manage.py runserver launched for you, entrypoint as a manage.py path, a required types_module pointing at views.py - and why that contract also fixes stale-type regen.
Flask + Next.js with flask run under the hood
Full walkthrough of examples/flask-nextjs on v0.13.0: one command launches Flask native and regenerates types, with directory autocomplete in the wizard and live config swaps to try.
Migrating your client to the links API
The mechanical before/after for v0.13.0: constructor options become a one-element links array, adapters re-export the terminators, and URL normalization gets looser not stricter.
Editing pyrpc.json while the server runs
The config watcher diffs parsed BackendSpec values, restarts only when the backend changes, and re-wires codegen for client edits - plus the three races its tests had to kill.
A filesystem prompt that can’t escape its jail
Client-root autocomplete via questionary.path: realpath containment filters symlink escapes from suggestions, while typed input stays free and only existence-checked. Tested headlessly.
The types_module contract
Registration is an import side effect. Why Django layouts regenerated stale types under the old model, and how naming the registration module fixes reload semantics honestly.
Explicit beats magic: declaring your backend in pyrpc.json
The nested backend/clients schema, sniffing demoted from oracle to preselection, --yes that fails closed, and BackendSpec validation as a data model instead of scattered ifs.
Your framework’s dev server, not ours
pyrpc dev now launches uvicorn, flask run, or manage.py runserver natively. Why hosting your stack beats substituting for it, and how LaunchPlan made the matrix testable.
Batched RPC requests, end to end
httpBatchLink coalesces same-tick operations into one JSON-array POST; the interpreter dispatches them sequentially with per-operation errors. What batching is - and deliberately is not.
Terminating links: giving @pyrpc/client a pipeline
The tRPC-style link architecture behind v0.13.0: exactly one terminating link owns transport, composable links handle auth/retry/logging, and URL normalization moved where it belongs.
A crash hiding in plain sight: watch vs watchfiles
How a local function shadowing the watchfiles import broke pyrpc watch, why every test missed it, and the three habits that catch shadowing bugs early.
What pyRPC owns, and what it doesn’t
The dependency contract: what ships in dependencies, what stays a peer, why TanStack Query is internal but not owned, and how the Python extras mirror it.
baseUrl: compile-time and runtime
A string in an options type at compile time, a normalization pipeline at runtime, and why the browser fallback fails loudly on the server.
How we checked every bundler: the verification matrix
The claim that tsconfig paths work for webpack but not Vite, SvelteKit, or Turbopack, proven by unit tests, example apps, and a matrix that shows why.
The release PR: how a version bump goes to review
Why the release is a reviewed merge followed by a tag, and how decoupling the two makes an irreversible step reversible.
Reconciling zero-config with a runtime module
A release that demands more configuration while moving toward less, and the safety mechanisms that make a tool-owned config trustworthy.
The changelog as structured data
Why the changelog is a TypeScript module, not Markdown: typechecked, programmatic, and rendered consistently by the docs page.
The version contract between npm workspaces and uv
An unenforced agreement that binds two package managers: how the shared version number is structured and where drift could creep in.
Auto-generated release notes and the naming fix
The softprops action, conventional-commit release notes, and the one-line change that renamed releases to their bare tags.
A tag is a release trigger
How a git tag matching v*.*.* fires PyPI, npm, and the GitHub Release, and how the prerelease signal flows through all three.
skip-existing and the npm guard: idempotent publishing
Two registries, two idempotency strategies, and why a release pipeline that can be re-run must reconcile instead of demand.
The publish chain: types to client to react to adapters
The needs: graph that serializes npm publishing, and why every job hermetically rebuilds its dependency chain from the tag.
Eleven packages, two ecosystems
Six npm workspaces and five Python packages shipped in lockstep from one tag, the dependency DAGs and the version contract that binds them.
The lockfile dance after a version bump
Why the bump script skips lockfiles, and how uv lock and npm install close the resolution gap before the release PR opens.
A version in three places
pyproject.toml, __init__.py, and uv.lock each hold the version for a different consumer, and the release process keeps them agreeing.
release.mjs: one command, eleven packages
How an 85-line script walks the packages directory and rewrites versions, internal ranges, and the root workspace in lockstep.
@pyrpc/types: from type-only to runtime dependency
A one-line package.json move that announces the whole v0.12.0 thesis, the type boundary became a runtime contract.
Why adapters keep @pyrpc/types external
The tsup externals that let the app bundle redirect the type package: how externalization makes runtime substitution possible.
The kinds override seam and why tests use it
A documented @internal escape hatch that turns the adapter into a pure function of kind, making hook-selection behavior exhaustively testable.
ProcedureHooksForKind: the type-level mirror
The conditional types that make TypeScript agree with the runtime Proxy, one decision, two languages, no drift.
How adapters read procedureKinds at runtime
The value import, the kinds override seam, and the Proxy get trap that builds query or mutation hooks from a single runtime lookup.
Idempotent re-wiring on every regen
Every codegen run reconfigures tsconfig and the bundler. How existing-value probes, alias guards, and write-on-change make that safe.
Why the alias is relative
The leading dot in "./__pyrpc.ts" is not style, it is the difference between a sibling file and a package that was never published.
When the config is too weird: failing loud
A two-state return value, a yellow warning, and the exact remediation hint, the calibrated failure ladder when a config cannot be safely edited.
Two alias shapes: Vite resolve.alias vs Turbopack resolveAlias
The same aliasing idea expressed in two bundler dialects, and why a shared splice core plus a per-tool snippet table is the right structure.
Inserting into a config object without parsing it
How a one-line splice at the closing brace, an idempotency guard, and write-on-change logic keep config edits safe across hundreds of regenerations.
A mini JS tokenizer for safe config editing
The hand-rolled tokenizer behind bundler config edits: skipping strings, comments, and template-literal interpolations so brace matching never misfires.
Detecting the bundler by config filename
Six filenames, two frameworks: how pyrpc decides which bundler you use by walking the client directory for vite.config and next.config signatures.
The node_modules resolution gap
Why tsconfig paths work for TypeScript and webpack but not for imports originating inside node_modules, and the bundler alias that closes the gap.
Two channels: compile-time types and runtime kinds
TypeScript erases types at runtime. procedureKinds is the runtime channel that mirrors the compile-time Types channel, and the generated module is where both meet.
Fail closed: why the placeholder throws
v0.9.0's placeholder exposed both hooks on every procedure; v0.12.0's throws. A comparison of two failure modes and why the strict one is the safe default.
The placeholder that throws
Before pyrpc dev runs, @pyrpc/types ships a Proxy placeholder whose procedureKinds throws on access, so a misconfigured resolution fails loud instead of silently showing every hook.
The _pyrpcKind brand and type-level kind inference
Every generated procedure carries a readonly _pyrpcKind brand. InferProcedureKinds reads it in a mapped type so the compile-time type system knows whether .useQuery or .useMutation exists.
Parsing Python type strings into TypeScript
The string-grammar parser behind _pytype_to_ts: how Optional, Union, List, Dict, Tuple, and Set annotations become nullable unions, arrays, and records, and where it falls back to any.
Inside the codegen template
How client.ts.j2 renders Types, the _pyrpcKind brand, ProcedureKinds, and the procedureKinds const from a Python schema dict.
The generated file's contract
The header of __pyrpc.ts is not decoration: it documents how the file is resolved, when it is regenerated, and what happens if you edit it by hand.
From .d.ts to .ts: when types became a runtime module
Why v0.12.0 stops emitting a declaration-only __pyrpc.d.ts and generates a real __pyrpc.ts module that carries both compile-time types and the runtime procedureKinds map.
The life of pyrpc.json: from wizard to watcher to CI
How a small JSON file is born in the wizard, found by walking up, read by every command, and hot-reloaded by a running dev session.
The dev console: a control panel, not a shell
Six commands (procedures, inspect, generate, restart, exit) and why the console deliberately stops at the dev loop instead of becoming a REPL.
Manual entry as a first-class wizard action
How v0.11.1 makes manual client entry a first-class wizard action, chosen before the checkbox list, so detected-project selections are never silently discarded.
Threads, timers, and the missing import time: how regeneration stays safe
The debounce timer, the timer lock, the regen lock, and the one-line import bug that silently silenced the regen log.
Reloading modules in the watcher: import vs reload
Why a fresh import is not enough, how importlib.reload refreshes procedures without restarting the server, and the tradeoffs of reloading modules.
Surgical tsconfig edits: injecting @pyrpc/types with jsonc-edit
The tsconfig.json edit that lets generated types reference @pyrpc/types, when it happens, and the edge cases we handle.
Multi-client support: one Python server, many frontends
The clients array in pyrpc.json, per-client __pyrpc.d.ts generation, and the live-watch reload when clients change.
pyrpc dev --yes: non-interactive setup for CI and scripts
Fully explicit --yes --module --client, auto-detected clients and frameworks, and a hard error instead of a guess when multiple frontends exist.
The zero-codegen workflow: save, wait 300ms, types are fresh
How the watch scope, .py filtering, and a 300ms debounce turn "edit Python, save" into regenerated TypeScript with no codegen command at all.
Django + Next.js: RSC prefetch with a Django backend
The django-nextjs example: Django async views with pyRPC, createNextClient pointing at Django, server-side prefetch in RSC, and client-side useQuery/useMutation.
pyrpc watch: type generation without the server
The type-watcher half of pyrpc dev with the server management removed: the same regeneration pipeline, minus uvicorn, port probing, and the interactive console.
Django + Svelte: async Python backend, reactive Svelte stores
The django-svelte example: Django 4.2+ async views, createSvelteClient, and the $ store subscription pattern with TanStack Svelte Query.
Django + Vue: async Django backend, Vue 3 composables
The django-vue example: Django async views with pyRPC, createPyrpcVue with app.use(pyrpc.plugin), and reactive createQuery composables.
Django + React: native async views, typed React hooks
The django-react example: async @rpc.query/@rpc.mutation views, why you must import views in urls.py, django-cors-headers setup, and createReactClient.
Flask + Next.js: App Router with a Flask backend
The flask-nextjs example: mount_flask on port 5000, createNextClient with the Flask baseUrl, server-side prefetch, and client-side hooks.
Probing the server: how pyrpc dev knows uvicorn is already running
One HTTP probe, a one-second timeout, and a server_managed flag that decides whether pyRPC starts uvicorn or attaches in watcher-only mode.
Flask + Svelte: minimal server, typed Svelte stores
The flask-svelte example: a two-file Flask server, createSvelteClient, and reactive stores, the smallest possible pyRPC stack.
Flask + Vue: zero-ceremony Python backend, Vue 3 frontend
The flask-vue example: mount_flask with flask-cors, createPyrpcVue pointing at port 5000, and Vue Composition API composables.
Flask + React: lightweight Python, full type safety
The flask-react example explained: mount_flask, flask-cors, createReactClient on port 5000, and the same useQuery/useMutation hooks as every other React adapter.
FastAPI + Svelte: typed Python procedures as Svelte stores
createSvelteClient, QueryClientProvider in +layout.svelte, createQuery/createMutation stores, and the $ subscription pattern, the fastapi-svelte example explained.
Types in your source tree: why generated types left node_modules
The design decision behind committing generated types to the client repo, and what it means for editors, CI, and deployment.
FastAPI + Vue: TanStack Vue Query with a Python backend
createPyrpcVue, pyrpc.plugin, createQuery with reactive args, and createMutation, the complete fastapi-vue example explained.
FastAPI + Next.js: RSC prefetch with a Python backend
How api.prefetch, api.dehydrate(), and api.HydrationBoundary work together to give you instant data in Server Components, with a FastAPI backend.
Framework auto-detection: reading the room from config files
How pyrpc recognizes Vite, Next.js, React, Vue, Svelte, and other frontends from package.json before the wizard has to ask.
FastAPI + React: full-stack type safety from zero
Step-by-step: FastAPI server with @rpc.query/@rpc.mutation, createReactClient, api.Provider, and useQuery/useMutation, the complete fastapi-react example explained.
Designing the pyrpc dev setup wizard: two questions, zero friction
How the first-run wizard asks the module and client questions, why --reconfigure pre-fills, and where detection takes over from prompts.
uvicorn --reload by default: how pyrpc dev manages the server
How pyrpc dev spawns uvicorn with --reload by default, the two independent reload paths, the restart command, and when to choose --no-reload.
From tRPC to pyRPC: what stays, what goes, what gets easier
Side-by-side comparison for tRPC users: same hooks, Python server, no Zod, no links chain.
Why we chose TanStack Query, and what it gives you for free
Caching, stale-while-revalidate, deduplication, and mutation invalidation without reinventing the wheel.
Testing procedure kinds: what we covered
Tests across four layers: Python decorators, introspection schema, codegen output, and adapter proxy.
Backward compatibility: how @rpc stayed working through v0.9.0
Why bare @rpc, no-kinds adapters, and existing installs all work without changes.
The CI/CD publish pipeline: tag-triggered, chained, OIDC
How publish.yml chains 6 jobs: PyPI via OIDC, npm types/client/react/adapters, and GitHub Release.
How we publish: from git tag to npm and PyPI
The full release flow: release.mjs, tag push, CI chain, OIDC for PyPI, and manual fallback.
The Svelte adapter: framework-native, zero ceremony
How createSvelteClient integrates with TanStack Svelte Query stores and Svelte-native patterns.
The Vue adapter: same contract, Vue-native patterns
How createPyrpcVue uses plugins instead of providers, composables instead of hooks.
Building the Next.js example app: file by file
Walkthrough of examples/fastapi-nextjs: server.py, lib/pyrpc.ts, layout, RSC prefetch, and client hooks.
The tiny change in @pyrpc/types that powers everything
Why ProcedureKinds and procedureKinds are in @pyrpc/types, and how the const satisfies pattern works.
The npx daemon: 715x faster type generation
How a persistent Node.js process drops jsonschema-ts from 3.3s to 4.6ms.
Inside the codegen template: what client.ts.j2 generates
What the Jinja2 template produces: Types interface, ProcedureKinds, procedureKinds, and model interfaces.
Router.merge: how pyRPC handles namespaces
Split procedures into separate modules and combine them with Router.merge() for larger projects.
How procedure kinds flow from Python to TypeScript
Trace @rpc.query/@rpc.mutation from Python decorator through introspection, codegen, and into typed hooks.
Publishing guide: npm @pyrpc/* and PyPI
release.mjs, build order, GitHub Actions tag publish, and manual npm/twine steps for new adapter packages.
v0.9.0, Framework adapters, procedure kinds, one api object
Release notes for @pyrpc/react, next, vue, svelte, server kinds, and the unified api DX.
Migrating from createClient to TanStack hooks
Keep Promise calls via api.client / createCaller while adopting useQuery, and annotate mutations on the server.
Vue and Svelte adapters: same contract, stack-native setup
createPyrpcVue with api.plugin, createSvelteClient with createQuery/createMutation, and TanStack conventions per framework.
Guide: @pyrpc/react from zero to useQuery
Minimal install, api.Provider, useQuery/useMutation, and useUtils for a Vite or CRA-style React app.
Next.js RSC: prefetch, dehydrate, and HydrationBoundary
What each server helper does, when you can skip hydration, and why prefetch is for queries not mutations.
@rpc.query and @rpc.mutation: why procedure kinds exist
How server-side query/mutation kinds flow through codegen into TanStack hooks without a second import in app code.
One api object: Provider, prefetch, and hooks in the same place
Why createNextClient / createReactClient return a single value with procedures, Provider, and server helpers, and why the variable can be named api or client.
How to version, edit, and ship pyRPC’s multi-package surface
Synchronized npm @pyrpc/* versions, PyPI independence, what to edit where, and PR/release standards for a multi-package monorepo.
Tutorial: Next.js App Router + TanStack Query with pyRPC
Step-by-step: @rpc.query/@rpc.mutation, createNextClient, RSC prefetch, HydrateClient, and client hooks.
Deep dive: framework adapters, TanStack Query, and procedure kinds
Architecture of @pyrpc/react, next, vue, and svelte, one transport, flat DX, query/mutation kinds, and Next.js hydration.
Following an RPC Call: From TypeScript Client to Python Function and Back
A step-by-step trace through pyrpc's RPC Call Flow: Proxy interception, HTTP transport, envelope validation, Router lookup, Procedure execution with TypeAdapters, and the error path.
A Visual Tour of pyrpc's Architecture
Eight diagrams, seven packages, one framework: a guided walkthrough of pyrpc's architecture from the system landscape down to individual components and dynamic flows.
Architecture as Code: Mapping pyrpc with LikeC4
How we turned pyrpc's architecture into version-controlled, interactive diagrams using LikeC4, and why every framework should do the same.
715x faster type generation with the npx daemon
A persistent Node.js daemon replaces per-call npx subprocesses, dropping type regeneration from 3.3s to 4.6ms. How it works, the benchmarks, and the edge cases we handled.
v0.7.3 - Django adapter, FastAPI/Flask fixes
A new Django adapter (pyrpc-django-adapter) with native async views, plus an introspection crash fix that affected all adapters when no explicit router was provided.
v0.6.0 - Client distribution and package standardization
npx pyrpc sync, postinstall prompt, framework extras (pyrpc-core[fastapi], pyrpc-core[flask]), adapter auto-install, pyrpc.json config, distribution modes, and package standardization.
Server mode: type distribution across repositories
How server mode works: the schema endpoint stays in memory, pyrpc never writes to the client filesystem, and the client fetches types on demand via npx pyrpc sync.
Workspace mode: what happens when you run pyrpc dev
A step-by-step walkthrough of workspace mode: config resolution, client root validation, migration checks, file watcher loop, dev server startup, and CI compatibility.
Distribution modes: workspace and server
Two distribution modes for pyrpc: workspace (monorepo, types written directly to client) and server (separate repos, types fetched via HTTP). When to use each and how they work under the hood.
Why save_typescript_client() refuses relative paths
The hidden bug in os.getcwd() fallback paths - why a silent default is worse than a hard error, how the CLI layer resolves paths before calling the API, and the "fail fast on global state" design principle.
No pyrpc init needed: designing the integrated setup wizard
Why pyrpc embeds setup inside pyrpc dev instead of a separate init command: fewer context switches, --reconfigure pre-fills defaults, CLI flags skip the wizard entirely, and KeyboardInterrupt exits cleanly.
Three deployment architectures for pyrpc
Monorepo, separate repos, and published npm package - how pyrpc's config system and type generation handle all three workflows, and why server-side codegen was built before the client-side npx CLI.
Three cases, zero data loss: pyrpc's types migration strategy
What happens when you change client_root in pyrpc.json? Three cases with SHA256 comparison, interactive prompts only when needed, and a clean KeyboardInterrupt path that never leaves half-migrated state.
Path resolution in pyrpc: config-relative, not CWD-relative
Why resolving paths against pyrpc.json's directory (not os.getcwd()) is the only correct approach, how the pipeline produces absolute paths everywhere, and why save_typescript_client() enforces the contract at the boundary.
pyrpc.json: why we left pyproject.toml behind
Three problems with [tool.pyrpc] in pyproject.toml - fragile writing, ambiguous file ownership, and unclear path semantics - and why a dedicated pyrpc.json file with JSON, not TOML, was the right answer.
v0.3.3 - Cleaner types, no more /rpc/rpc, quieter watcher, CORS included
TypeScript autocomplete no longer suggests .rpc, URL normalization prevents double /rpc/rpc, file watcher debounced to 300ms, and the ASGI dev server now sends CORS headers - all following reference patterns from tRPC, Better Auth, FastAPI, webpack, and nodemon.
v0.3.2 - Cleaner terminal, smarter prompts, no more :app confusion
Interactive framework picker, simplified entry point, CWD import path fix, and a terminal that shows what matters - no Uvicorn spam, no raw [cyan] markup, no giant Panel boxes.
v0.3.1 - Lazy imports, pyrpc_codegen decoupled from CLI
pyrpc_codegen is no longer loaded for version, inspect, serve, pull, or help - only codegen and dev need it. A patch triggered by a stale shim bug.
v0.3.0 - pyrpc-cli merged into core, one-command install
pip install pyrpc-core now gives you the runtime, CLI, and codegen in a single command - no separate packages, no extra steps.
Why we merged pyrpc-cli back into pyrpc-core
How the circular dependency that motivated a three-package split disappeared - and why we simplified back to two packages for a single-install experience.
How to break a circular dependency in Python packaging
Four strategies for breaking circular package dependencies in Python, evaluated through pyrpc’s real-world restructuring, with a step-by-step extraction guide.
Windows compatibility in a Python OSS project: what we learned
Unicode crashes on cp1252, LF/CRLF git warnings, path separators, file watcher quirks, and a no-special-chars policy for cross-platform Python OSS.
Lazy imports as API contract, not performance hack
Three tiers of CLI commands, the packaging-vs-code dependency distinction, and why lazy imports define capability boundaries, not just startup time.
The Better Auth meta-package pattern, adapted for Python
How Better Auth’s npm meta-package inspired pyrpc’s package architecture, and how we adapted it for Python’s packaging constraints.
Core → CLI → Codegen: why the dependency direction matters
Why pyrpc-core → pyrpc-cli → pyrpc-codegen is the right dependency direction - and three principles for designing package chains that never tangle.
Dev console vs shell: two tools, one job, and the line between them
Why the dev console reads from the parent process (not HTTP), how the shell connects remotely, and the shared REPL UI that bridges them.
The circular dependency problem and how pyrpc-cli solved it
How we discovered and solved the circular dependency between pyrpc-core and pyrpc-codegen by extracting pyrpc-cli - with three alternative strategies evaluated and a step-by-step extraction guide.
CLI overhaul, model interfaces, and the dev tools we built
Merging pull into codegen, fixing serve, adding the dev watcher and shell REPL, and integrating jsonschema-ts for Pydantic model interfaces.
Designing the pyrpc developer console
Threads, subprocesses, and an embedded interactive console - how pyrpc dev combines a dev server, file watcher, type generator, and CLI into one terminal session.
v0.2.0 - Type safety, proper async, and @pyrpc/types
The three critical fixes that ship v0.2.0: real type generation, working async, and a postinstall-based @pyrpc/types setup.
Cleaner codegen, one CLI, and a sharper story
Pattern A CLI, lazy pyrpc-core imports, frontend DX simplified to npm install, cross-language positioning, SECURITY.md rewrite, and Windows cp1252 fixes.
Inside the Interactive Demo Sandbox
A deep dive into how the pyrpc playground works - design decisions, architecture, and a comparison with the real pyrpc implementation.
Why pyRPC?
The philosophy behind pyRPC, what tRPC-style typing means for Python backends, and why we built it.
From raw FastAPI to pyRPC
A before-and-after migration guide showing how to convert a traditional FastAPI application to pyRPC - and why you might want to.
Building a full-stack app with pyRPC
A step-by-step tutorial: FastAPI backend, TypeScript React frontend, end-to-end type safety with pyRPC.

pyRPC