modularizer-plat 0.1.0__tar.gz

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.
Files changed (57) hide show
  1. modularizer_plat-0.1.0/.gitignore +89 -0
  2. modularizer_plat-0.1.0/PKG-INFO +588 -0
  3. modularizer_plat-0.1.0/README.md +572 -0
  4. modularizer_plat-0.1.0/plat/__init__.py +217 -0
  5. modularizer_plat-0.1.0/plat/__main__.py +5 -0
  6. modularizer_plat-0.1.0/plat/auth.py +139 -0
  7. modularizer_plat-0.1.0/plat/call_sessions.py +139 -0
  8. modularizer_plat-0.1.0/plat/cli.py +408 -0
  9. modularizer_plat-0.1.0/plat/cli_main.py +475 -0
  10. modularizer_plat-0.1.0/plat/client.py +180 -0
  11. modularizer_plat-0.1.0/plat/date_coerce.py +318 -0
  12. modularizer_plat-0.1.0/plat/decorators.py +90 -0
  13. modularizer_plat-0.1.0/plat/errors.py +94 -0
  14. modularizer_plat-0.1.0/plat/file_queue.py +29 -0
  15. modularizer_plat-0.1.0/plat/file_queue_protocol_plugin.py +203 -0
  16. modularizer_plat-0.1.0/plat/file_transport_plugin.py +87 -0
  17. modularizer_plat-0.1.0/plat/http_transport_plugin.py +167 -0
  18. modularizer_plat-0.1.0/plat/literalenum_support.py +170 -0
  19. modularizer_plat-0.1.0/plat/logging.py +39 -0
  20. modularizer_plat-0.1.0/plat/metadata.py +26 -0
  21. modularizer_plat-0.1.0/plat/openapi_client.py +1016 -0
  22. modularizer_plat-0.1.0/plat/openapi_codegen.py +432 -0
  23. modularizer_plat-0.1.0/plat/plugins.py +523 -0
  24. modularizer_plat-0.1.0/plat/protocol_plugin.py +93 -0
  25. modularizer_plat-0.1.0/plat/py.typed +0 -0
  26. modularizer_plat-0.1.0/plat/response_serialize.py +76 -0
  27. modularizer_plat-0.1.0/plat/routing.py +45 -0
  28. modularizer_plat-0.1.0/plat/rpc.py +4 -0
  29. modularizer_plat-0.1.0/plat/rpc_protocol_plugin.py +169 -0
  30. modularizer_plat-0.1.0/plat/rpc_transport_plugin.py +68 -0
  31. modularizer_plat-0.1.0/plat/server.py +1444 -0
  32. modularizer_plat-0.1.0/plat/server_types.py +117 -0
  33. modularizer_plat-0.1.0/plat/tools.py +150 -0
  34. modularizer_plat-0.1.0/plat/transport_plugin.py +71 -0
  35. modularizer_plat-0.1.0/plat/transports.py +32 -0
  36. modularizer_plat-0.1.0/plat/type_registry.py +23 -0
  37. modularizer_plat-0.1.0/pyproject.toml +29 -0
  38. modularizer_plat-0.1.0/samples/README.md +109 -0
  39. modularizer_plat-0.1.0/samples/__init__.py +1 -0
  40. modularizer_plat-0.1.0/samples/basic/__init__.py +1 -0
  41. modularizer_plat-0.1.0/samples/basic/models.py +44 -0
  42. modularizer_plat-0.1.0/samples/basic/orders.api.py +25 -0
  43. modularizer_plat-0.1.0/samples/basic/products.api.py +52 -0
  44. modularizer_plat-0.1.0/samples/basic/server.py +23 -0
  45. modularizer_plat-0.1.0/samples/long_running/__init__.py +1 -0
  46. modularizer_plat-0.1.0/samples/long_running/client.py +27 -0
  47. modularizer_plat-0.1.0/samples/long_running/imports.api.py +47 -0
  48. modularizer_plat-0.1.0/samples/long_running/models.py +14 -0
  49. modularizer_plat-0.1.0/samples/long_running/server.py +23 -0
  50. modularizer_plat-0.1.0/tests/test_cli_helpers.py +68 -0
  51. modularizer_plat-0.1.0/tests/test_date_coerce.py +103 -0
  52. modularizer_plat-0.1.0/tests/test_literalenum_support.py +47 -0
  53. modularizer_plat-0.1.0/tests/test_openapi_client.py +290 -0
  54. modularizer_plat-0.1.0/tests/test_response_serialize.py +66 -0
  55. modularizer_plat-0.1.0/tests/test_samples.py +145 -0
  56. modularizer_plat-0.1.0/tests/test_server.py +449 -0
  57. modularizer_plat-0.1.0/tests/test_type_registry.py +49 -0
@@ -0,0 +1,89 @@
1
+ ############################
2
+ # Node
3
+ ############################
4
+
5
+ node_modules/
6
+ npm-debug.log*
7
+ yarn-debug.log*
8
+ yarn-error.log*
9
+ pnpm-debug.log*
10
+
11
+ ############################
12
+ # Build output
13
+ ############################
14
+
15
+ dist/
16
+ build/
17
+ coverage/
18
+
19
+
20
+
21
+ ############################
22
+ # TypeScript
23
+ ############################
24
+
25
+ *.tsbuildinfo
26
+
27
+ ############################
28
+ # Environment variables
29
+ ############################
30
+
31
+ .env
32
+ typpescript/.env.sample
33
+ !.env.example
34
+
35
+ ############################
36
+ # Logs
37
+ ############################
38
+
39
+ logs/
40
+ *.log
41
+
42
+ ############################
43
+ # Editors
44
+ ############################
45
+
46
+ .vscode/
47
+ .idea/
48
+ *.swp
49
+ *.swo
50
+ *.swn
51
+ *~
52
+
53
+ ############################
54
+ # OS files
55
+ ############################
56
+
57
+ .DS_Store
58
+ Thumbs.db
59
+
60
+ ############################
61
+ # Temporary files
62
+ ############################
63
+
64
+ tmp/
65
+ temp/
66
+
67
+ ############################
68
+ # Misc
69
+ ############################
70
+
71
+ *.pid
72
+ *.seed
73
+ *.lock
74
+
75
+
76
+
77
+ ###
78
+ **/*u-*
79
+ *-u.*
80
+ **/*untracked-*
81
+ *-untracked.*
82
+ **/*u_*
83
+ *_u.*
84
+ **/*untracked_*
85
+ *_untracked.*
86
+
87
+
88
+ *venv*
89
+ *.egg-info
@@ -0,0 +1,588 @@
1
+ Metadata-Version: 2.4
2
+ Name: modularizer-plat
3
+ Version: 0.1.0
4
+ Summary: Protocol and Language Agnostic Tooling Yielding Universal Semantics
5
+ Project-URL: Repository, https://github.com/modularizer/plat
6
+ License-Expression: Unlicense
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: fastapi>=0.115
9
+ Requires-Dist: httpx>=0.25
10
+ Requires-Dist: pydantic>=2.8
11
+ Requires-Dist: pyjwt>=2.8
12
+ Requires-Dist: pyyaml>=6.0
13
+ Requires-Dist: uvicorn>=0.30
14
+ Requires-Dist: websockets>=12.0
15
+ Description-Content-Type: text/markdown
16
+
17
+ # 🦫 plat (aka "platypus")
18
+ > **"call it like you're there"**
19
+
20
+ plat is a **Protocol and Language Agnostic Tooling Yielding Proxy-like Universal Semantics**. In short:
21
+ - just write your methods...
22
+ - ...then call them
23
+
24
+ #### It doesn't matter what you are designing
25
+ - Standard REST API
26
+ - MCP Server
27
+ - A CLI tool
28
+ - Database CRUD
29
+ - AI Tool Calls
30
+ - Inter Process Communication
31
+ - Worker Queues
32
+ - Client-to-client Chat Room
33
+
34
+ > At the end of the day, all there is is **handlers and callers**.
35
+
36
+ #### Yes there is an API layer there, but it isn't really your concern.
37
+ - auth
38
+ - exceptions
39
+ - headers
40
+ - serialization/deserialization/type coercion
41
+ - param validation
42
+ - route building
43
+ - response building
44
+ - rate limiting
45
+ - caching
46
+ - client-side retry logic
47
+ - client-side param validation
48
+
49
+ > All of that can be **handled easily behind the scenes** with easily standardizable **middleware plugins**.
50
+
51
+ #### In fact, the transport method itself doesn't even matter:
52
+ - HTTP
53
+ - WS
54
+ - File Queues
55
+ - DB triggers
56
+ - Zapier Integrations
57
+ - External APIs
58
+ - USPS mail delivery
59
+
60
+ > It's all important, but your function handler **does not need to know** about it, and **neither does your call site**.
61
+
62
+
63
+ #### Okay, seriously ... what are we even talking about here?
64
+ Fair... try this
65
+
66
+ ---
67
+ # Quickstart
68
+
69
+ ## 1. Install
70
+ ```bash
71
+ pip install modularizer-plat
72
+ ```
73
+
74
+ ---
75
+
76
+ ## 2. Make a server
77
+
78
+ ```python
79
+ from plat import Controller, POST, RouteContext, create_server
80
+
81
+
82
+ @Controller()
83
+ class OrdersApi:
84
+ @POST()
85
+ def createOrder(
86
+ self,
87
+ input: dict[str, str | int],
88
+ ctx: RouteContext,
89
+ ) -> dict[str, str]:
90
+ return {"orderId": "ord_123", "status": "pending"}
91
+
92
+
93
+ server = create_server({"port": 3000}, OrdersApi)
94
+ server.listen()
95
+ ```
96
+
97
+ ## 3. Serve it with the CLI
98
+
99
+ ```bash
100
+ plat serve
101
+ ```
102
+
103
+ ## 4. See the docs
104
+
105
+ ```bash
106
+ open http://localhost:3000/
107
+ ```
108
+
109
+ ## 5. Make a client
110
+
111
+ ```bash
112
+ plat gen client http://localhost:3000/ --dst client.py
113
+ ```
114
+
115
+ ```python
116
+ from client import create_client
117
+
118
+
119
+ client = create_client("http://localhost:3000")
120
+ order = client.createOrder(itemId="sku_123", qty=2)
121
+ print(order)
122
+ ```
123
+
124
+ ## 6. Make a CLI
125
+
126
+ ```bash
127
+ plat gen cli http://localhost:3000/ --dst cli.py
128
+ ```
129
+
130
+ ```bash
131
+ python cli.py createOrder --itemId=sku_123 --qty=2
132
+ ```
133
+
134
+ ## 7. Let your AI loose
135
+
136
+ ```python
137
+ from plat import create_openapi_client
138
+
139
+
140
+ client = create_openapi_client(
141
+ "http://localhost:3000/openapi.json",
142
+ "http://localhost:3000",
143
+ )
144
+
145
+ tools = client.tools
146
+ # hand `tools` to your AI provider
147
+ # then call back into `client.createOrder(...)` when it selects a tool
148
+ ```
149
+
150
+ ## Use it
151
+
152
+ - Client call: `client.createOrder(itemId="...", qty=2)`
153
+ - Generated CLI call: `plat createOrder --itemId=... --qty=2`
154
+ - AI tool call: an LLM can see `createOrder` as a tool with a name, input shape, and result shape
155
+ - Documentation: generated `openapi.json`, plus docs/tool metadata derived from the same method
156
+
157
+ ---
158
+
159
+
160
+ ## 🎯 Why plat exists
161
+
162
+ Most API frameworks make you think about:
163
+
164
+ - routes
165
+ - methods
166
+ - headers
167
+ - authentication
168
+ - serialization/deserialization/coercion
169
+ - sending responses
170
+ - REST hierarchies
171
+ - request shapes
172
+ - wire protocols
173
+ - client generation drift
174
+ - transport details
175
+
176
+ plat tries to make most of that disappear.
177
+
178
+ What you should be thinking about is:
179
+
180
+ - what methods exist
181
+ - what input each method accepts
182
+ - what result each method returns
183
+
184
+ That’s the part plat treats as sacred.
185
+
186
+ > It feels like you are adding a new class, and behind the scenes an API is born
187
+
188
+ One of the biggest reasons plat exists is to make it easy to use **any AI provider**:
189
+
190
+ - on the client side
191
+ - on the server side
192
+ - as the initiator of your tasks
193
+ - or as the doer of your tasks
194
+ - or both
195
+
196
+ Everything below is in service of that same promise: define useful methods once, then let clients, CLIs, docs, and AI tools all see the same surface.
197
+
198
+ ### Diagram
199
+
200
+ ```
201
+ ┌───────────────────────────────────────────────────────┐
202
+ │ Tool Definitions │
203
+ │ (controllers + decorated methods) │
204
+ │ │
205
+ │ TypeScript (plain types) Python (type hints) │
206
+ │ class Orders { @Controller() │
207
+ │ @Post() class Orders: │
208
+ │ create(input, ctx) {} @POST() │
209
+ │ @Get() def create(self): ... │
210
+ │ list(input, ctx) {} @GET() │
211
+ │ } def list(self): ... │
212
+ └──────────────────────────┬────────────────────────────┘
213
+
214
+ ┌──────────┴──────────┐
215
+ │ Operation Registry │
216
+ │ │
217
+ │ operationId ─────► bound handler
218
+ │ method+path ─────► bound handler
219
+ └──────────┬──────────┘
220
+
221
+ server protocol plugins (how tool calls arrive)
222
+
223
+ ┌───────┬────────┬─────┴───┬────────┬───────┬───────┐
224
+ │ │ │ │ │ │ │
225
+ ┌───┴──┐┌───┴──┐┌────┴───┐┌────┴───┐┌───┴──┐┌───┴──┐┌───┴──┐
226
+ │ HTTP ││ WS ││ File ││ WebRTC ││ DB ││BullMQ││ MQTT │
227
+ │ REST ││ RPC ││ Queue ││ Data ││ Poll ││ Redis││Pub/ │
228
+ │ ││ ││ ││ Chan ││ Rows ││Queue ││ Sub │
229
+ └──────┘└──────┘└────────┘└────────┘└──────┘└──────┘└──────┘
230
+ ...
231
+ literally anything that can carry a JSON envelope
232
+ ...
233
+ ┌──────┐┌──────┐┌────────┐┌────────┐┌──────┐┌──────┐┌──────┐
234
+ │ HTTP ││ WS ││ File ││ WebRTC ││ POST ││ eBay ││ FB │
235
+ │ fetch││ RPC ││ IO ││ Peer ││to ext││ list ││ Msg │
236
+ │ ││ ││ ││ Conn ││ API ││ poll ││ poll │
237
+ └───┬──┘└───┬──┘└────┬───┘└────┬───┘└───┬──┘└───┬──┘└───┬──┘
238
+ │ │ │ │ │ │ │
239
+ └───────┴────────┴────┬────┴────────┴───────┴───────┘
240
+
241
+ client transport plugins (how tool calls are sent)
242
+
243
+ ┌──────────┴──────────┐
244
+ │ OpenAPI Client │
245
+ │ (typed proxy) │
246
+ └──────────┬──────────┘
247
+
248
+ ┌─────────┬────────┼────────┬───────────┐
249
+ │ │ │ │ │
250
+ ┌────┴───┐┌────┴───┐┌───┴───┐┌───┴────┐┌─────┴─────┐
251
+ │ TS ││ Python ││ CLI ││ curl ││ LLM Agent │
252
+ │ ││ ││ ││ bash ││ │
253
+ │ node ││ sync ││ plat do ││ write ││ Claude │
254
+ │ bun ││ async ││plat poll││ JSON ││ ChatGPT │
255
+ │ browser││ promise││ ││to inbox││ Gemini │
256
+ └────────┘└────────┘└───────┘└────────┘└───────────┘
257
+ ```
258
+
259
+ The transport protocol, serialization, deserialization, queueing, and delivery mechanics are intentionally pushed out of your way.
260
+
261
+ That is especially powerful for AI-heavy systems, because you can keep swapping providers and execution patterns while preserving the same tool-shaped surface.
262
+
263
+
264
+ ## 🎭 What the user experience should feel like
265
+
266
+ It should feel like this:
267
+
268
+ ```python
269
+ order = client.createOrder(itemId="sku_123", qty=2)
270
+ ```
271
+
272
+ Not like this:
273
+
274
+ - choosing between totally different client libraries
275
+ - hand-authoring RPC envelopes
276
+ - thinking about HTTP vs WS every time you call a method
277
+ - manually syncing method names, routes, SDK methods, and OpenAPI operation IDs
278
+ - re-implementing error handling, retries, and auth every time you make a request
279
+ - refactoring if you change languages or protocols
280
+ - endless boilerplate
281
+
282
+ It's like an SDK except you don't have to write it. It just comes for free with every `openapi.json`.
283
+
284
+ ## 🦫 Flat by design
285
+
286
+ plat is intentionally opinionated about the API shape.
287
+
288
+ <details open>
289
+ <summary><strong>The rules</strong></summary>
290
+
291
+ - Method names are globally unique
292
+ - Method names are the canonical route names
293
+ - Input comes in as one object
294
+ - Return values matter as first-class API types
295
+ - Controllers organize code and docs, not URL hierarchies
296
+ - The API surface stays flat and easy to call
297
+
298
+ </details>
299
+
300
+ ### Example
301
+
302
+ ```python
303
+ from plat import Controller, GET, POST, RouteContext
304
+
305
+
306
+ @Controller()
307
+ class OrdersApi:
308
+ @GET()
309
+ def getOrder(self, input: dict[str, str], ctx: RouteContext) -> dict[str, str]:
310
+ return {"id": input["id"], "status": "pending"}
311
+
312
+ @POST()
313
+ def createOrder(self, input: dict[str, str | int], ctx: RouteContext) -> dict[str, str]:
314
+ return {"id": "ord_123", "status": "pending"}
315
+ ```
316
+
317
+ Canonical routes:
318
+
319
+ - `GET /getOrder`
320
+ - `POST /createOrder`
321
+
322
+ Canonical client calls:
323
+
324
+ ```python
325
+ client.getOrder(id="ord_123")
326
+ client.createOrder(itemId="sku_123", qty=2)
327
+ ```
328
+
329
+ That flatness matters because it makes the generated and dynamic clients obvious:
330
+
331
+ - easy for humans to remember
332
+ - easy for CLIs to expose
333
+ - easy for AI agents to understand
334
+ - easy for generated clients to mirror exactly
335
+ - easy to hand to any AI provider as tool definitions
336
+
337
+ ## ⏳ Long-running calls without changing the mental model
338
+
339
+ Sometimes a method is fast:
340
+
341
+ ```python
342
+ client.createOrder(itemId="sku_123", qty=2)
343
+ ```
344
+
345
+ Sometimes a method is slow, and you want visibility:
346
+
347
+ ```python
348
+ import logging
349
+
350
+
351
+ logger = logging.getLogger("plat.example")
352
+
353
+ client.importCatalog(
354
+ source="s3://bucket/catalog.csv",
355
+ _rpc_events=lambda event: logger.info("%s %s", event.event, event.data),
356
+ )
357
+ ```
358
+
359
+ Or you want deferred execution:
360
+
361
+ ```python
362
+ handle = client.importCatalog(
363
+ source="s3://bucket/catalog.csv",
364
+ _execution="deferred",
365
+ )
366
+
367
+ result = handle.wait()
368
+ ```
369
+
370
+ The important part is that it is still the same method.
371
+
372
+ As a bonus, in the right mode you can get:
373
+
374
+ - progress updates
375
+ - logs
376
+ - chunks/messages
377
+ - cancellation
378
+
379
+ That is what most users actually care about. The carrier and plugin details are for transport authors.
380
+
381
+ ## 🐍 Python support
382
+
383
+ plat supports Python servers and clients too.
384
+
385
+ You can:
386
+
387
+ - write Python controllers with plat decorators
388
+ - generate OpenAPI from `*.api.py`
389
+ - generate Python clients from OpenAPI
390
+ - use sync, async, and promise-style Python clients
391
+
392
+ <details>
393
+ <summary><strong>Python highlights</strong></summary>
394
+
395
+ - Sync clients
396
+ - Async clients
397
+ - Promise-style clients
398
+ - Deferred call handles
399
+ - Automatic input coercion
400
+ - Automatic output serialization
401
+ - First-class HTTP exception types
402
+
403
+ </details>
404
+
405
+ ## 🔌 One client, many transports
406
+
407
+ The same method call should stay usable even when transport changes.
408
+
409
+ ```python
410
+ http_client = create_client("http://localhost:3000")
411
+ rpc_client = create_client("ws://localhost:3000")
412
+ file_client = create_client("file:///tmp/plat-queue")
413
+
414
+ http_client.createOrder(itemId="sku_123", qty=2)
415
+ rpc_client.createOrder(itemId="sku_123", qty=2)
416
+ file_client.createOrder(itemId="sku_123", qty=2)
417
+ ```
418
+
419
+ Same tool call. Different carrier.
420
+
421
+ ### Diagram
422
+
423
+ ```txt
424
+ createOrder({ itemId, qty })
425
+
426
+ ┌───────┼────────┐
427
+ │ │ │
428
+ ▼ ▼ ▼
429
+ HTTP WS File
430
+ │ │ │
431
+ └───────┼────────┘
432
+
433
+ same type-aware method call
434
+ ```
435
+
436
+ ## 🤖 AI tool calling
437
+
438
+ plat is a natural fit for LLM tools because the API shape is already tool-shaped.
439
+
440
+ Every operation has:
441
+
442
+ - a stable name
443
+ - one input object
444
+ - one result
445
+ - generated schema
446
+
447
+ That means you can use AI providers in whichever role you want:
448
+
449
+ - as the caller deciding what tools to use
450
+ - as the worker fulfilling part of a task
451
+ - as interchangeable providers inside the same larger workflow
452
+ - on the client side or the server side
453
+
454
+ That makes the same API useful to:
455
+
456
+ - normal app code
457
+ - a CLI
458
+ - generated SDKs
459
+ - an LLM agent
460
+
461
+ ## 🧰 Dynamic clients and generated clients
462
+
463
+ plat supports both styles.
464
+
465
+ <details>
466
+ <summary><strong>Dynamic clients</strong></summary>
467
+
468
+ The OpenAPI client can work directly from an OpenAPI document and a runtime proxy.
469
+
470
+ Best when you want:
471
+
472
+ - low ceremony
473
+ - transport flexibility
474
+ - no generated wrapper code
475
+
476
+ </details>
477
+
478
+ <details>
479
+ <summary><strong>Generated clients</strong></summary>
480
+
481
+ plat can also generate clients that materialize types and methods.
482
+
483
+ Especially useful in Python, where explicit generated models and wrappers help more than in TypeScript.
484
+
485
+ </details>
486
+
487
+ ## 🖥️ CLI
488
+
489
+ plat includes a spec-first CLI.
490
+
491
+ ```bash
492
+ plat gen openapi
493
+ plat gen client
494
+ plat gen cli
495
+ plat run openapi.json
496
+ plat serve
497
+ ```
498
+
499
+ The CLI is available from both Node and Python packaging surfaces, with capability moving toward parity.
500
+
501
+ ## 🧩 Plugin architecture
502
+
503
+ The plugin architecture matters, but mostly as an implementation and extension story.
504
+
505
+ For normal plat users, the important thing is:
506
+
507
+ - methods stay flat
508
+ - typing stays strong
509
+ - clients feel direct
510
+ - transport details stay hidden
511
+ - provider complexity stays hidden too
512
+
513
+ For plugin developers, plat provides the escape hatch.
514
+
515
+ ### Client-side transport plugins
516
+
517
+ Transport plugins follow a generic lifecycle:
518
+
519
+ - connect
520
+ - send request
521
+ - receive updates
522
+ - receive result
523
+ - disconnect
524
+
525
+ ### Server-side protocol plugins
526
+
527
+ Protocol plugins are how tool calls arrive and how updates/results leave.
528
+
529
+ The goal is for the core method/typing/invocation story to be independent from:
530
+
531
+ - HTTP
532
+ - WebSockets
533
+ - Node
534
+ - any specific host process
535
+
536
+ <details>
537
+ <summary><strong>Why this matters</strong></summary>
538
+
539
+ That is what enables ideas like:
540
+
541
+ - a browser-side server
542
+ - a mobile-hosted server
543
+ - a worker-hosted server
544
+ - IndexedDB-backed local APIs
545
+ - WebRTC-based peer-to-peer tools
546
+ - custom carriers like DB polling or Redis streams
547
+
548
+ Most users should not have to think about any of this unless they are building a transport.
549
+
550
+ </details>
551
+
552
+ ## 🌍 What makes plat different
553
+
554
+ Most systems force you to choose:
555
+
556
+ - REST or RPC
557
+ - server or client
558
+ - app integration or AI tool integration
559
+ - HTTP or "something custom"
560
+
561
+ plat is trying to collapse those choices into one model:
562
+
563
+ 1. Define useful tools
564
+ 2. Expose them everywhere
565
+ 3. Change carriers without changing the API itself
566
+
567
+ That makes plat especially interesting for:
568
+
569
+ - internal tools
570
+ - AI agents
571
+ - automation systems
572
+ - offline-first systems
573
+ - browser-hosted local APIs
574
+ - weird protocol experiments
575
+
576
+ ## 🛣️ Direction
577
+
578
+ plat is actively moving toward:
579
+
580
+ - deeper transport neutrality
581
+ - stronger portable server core extraction
582
+ - easier custom protocol plugins
583
+ - stronger generated clients and CLIs
584
+ - better cross-language symmetry
585
+
586
+ The north star is simple:
587
+
588
+ > **Define tools once. Call them from anywhere. Carry them over anything.**