PHPackages                             milpa/console - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. milpa/console

ActiveLibrary[Framework](/categories/framework)

milpa/console
=============

The projection layer of the Milpa PHP framework: surface projectors that turn one declared Operation into the shape each surface speaks — CLI flags, MCP tools — plus the signature gate that makes consent name the call.

v0.1.0(today)014↑2900%1Apache-2.0PHPPHP &gt;=8.3CI passing

Since Jul 30Pushed todayCompare

[ Source](https://github.com/getmilpa/console)[ Packagist](https://packagist.org/packages/milpa/console)[ RSS](/packages/milpa-console/feed)WikiDiscussions main Synced today

READMEChangelog (6)Dependencies (12)Versions (2)Used By (1)

 [   ![Milpa](https://raw.githubusercontent.com/getmilpa/core/main/art/lockup/milpa-lockup-v-color-light.svg)  ](https://github.com/getmilpa)

Milpa Console
=============

[](#milpa-console)

> The **projection layer** of Milpa. [`milpa/command`](https://github.com/getmilpa/command) declares the atom — one surface-agnostic `Operation`; this package turns that atom into the shape each surface actually speaks. Today: **CLI** (flags, argument coercion, the signature gate) and **MCP**(tools an agent can call). One declaration, N surfaces.

[![CI](https://github.com/getmilpa/console/actions/workflows/ci.yml/badge.svg)](https://github.com/getmilpa/console/actions/workflows/ci.yml)[![Packagist](https://camo.githubusercontent.com/bf9b537c75872b9abc86828326e5c36735173651aeab86e5c82fe0d493c126ff/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d696c70612f636f6e736f6c65)](https://packagist.org/packages/milpa/console)[![License](https://camo.githubusercontent.com/35b94db23d3ed026343335f74d52ce31e74b77ad7dab4e4b89f49f2026e0937f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4170616368652d2d322e302d626c7565)](LICENSE)

Install
-------

[](#install)

```
composer require milpa/console
```

Quick example
-------------

[](#quick-example)

A plugin declares an operation once, through `milpa/command`'s `CommandProvider`. It does not say anything about flags, JSON-schema or terminals:

```
use Milpa\Command\Operation;

new Operation(
    name: 'create_post',
    description: 'Create a draft post',
    handler: [PostService::class, 'create'],
    inputSchema: ['type' => 'object', 'properties' => ['title' => ['type' => 'string']]],
    mutating: true,
    requiresConfirmation: true,
);
```

The projectors give it a surface:

```
use Milpa\Console\CliProjector;
use Milpa\Console\McpProjector;

// CLI: derives `--title=…` from the schema, coerces the string argv into typed input, and
// enforces the signature gate before a mutating operation runs.
(new CliProjector($authorizer))->run($operation, $argv, $container, $write);

// MCP: the same operation becomes a tool an agent can list and call.
(new McpProjector())->project($operations, $registry, $container);
```

Consent names the call
----------------------

[](#consent-names-the-call)

On a terminal, `requiresConfirmation: true` is not a `--yes` flag. A flag consents in the abstract — the same yes covers removing any plugin on any host — so `CliProjector` asks for a **signature that names this call**: the operation, its arguments, the host and a nonce. `SchemaCoercer` turns argv strings into the types the schema declares before any of that, so what gets signed is what runs.

The pieces are seams, not concretions: `OperationSigner` is the port, [`GnupgOperationSigner`](src/GnupgOperationSigner.php) an adapter, and verification and nonce spending live behind `milpa/tool-runtime`'s `OperationAuthorizer`.

Testing your own surfaces
-------------------------

[](#testing-your-own-surfaces)

`Milpa\Console\Testing\SignsOperations` ships in `src/` on purpose: Composer does not autoload a dependency's `autoload-dev`, so a test helper that lives in `tests/` is unreachable for whoever consumes the package. The trait hands you an always-signing signer and an accepting authorizer, so a test that just needs to get past the gate can do so without a real key.

Where this is going
-------------------

[](#where-this-is-going)

[ADR-0035](https://github.com/getmilpa/governance) — *a projection is a value, not an effect* — governs this package. Today `CliProjector::run()` executes and `McpProjector::project()` registers; neither returns a surface model, and that is the thing being retrofitted: a projector will produce a model and a renderer will materialize it, so a surface can change its renderer without touching its projector.

HTTP, without dragging identity in
----------------------------------

[](#http-without-dragging-identity-in)

`Milpa\Console\Http\HttpProjector` is the fourth surface: one route per operation, plus the generic controller those routes point at. It arrived in 0.4.0 — until then it lived in `milpa/skeleton`, because moving it as-is would have dragged `milpa/auth` into a floor meant to run without it.

What made the move possible is that identity now sits behind an interface. The projector knows nothing about who is calling; `OperationHttpPolicy` does, and [`milpa/admin`](https://packagist.org/packages/milpa/admin) publishes the implementation that uses `milpa/auth`. Write your own and the projector will use it.

```
use Milpa\Console\Http\HttpProjector;

// $psr17 is any PSR-17 factory pair you already have (Nyholm, Guzzle, Laminas…).
$projector = new HttpProjector($operations, $container, $psr17, $psr17, policy: $yourPolicy);

$routes = $projector->routes();     // hand these to your router
$model  = $projector->project($op); // or just ask what an operation would expose
```

Unlike the other optional collaborators in this family, **not knowing is not permission here**: an operation that declares scopes with no policy wired throws `UnguardedOperationException` (a 500) rather than running unguarded. It stays a 500 and never a 401/403 — the caller did nothing wrong; the host declared something protected and left it without a guard. An operation that declares neither scopes nor a permission never touches any of this.

Requirements
------------

[](#requirements)

- PHP &gt;= 8.3
- [`milpa/command`](https://github.com/getmilpa/command), [`milpa/core`](https://github.com/getmilpa/core), [`milpa/tool-runtime`](https://github.com/getmilpa/tool-runtime), [`milpa/http`](https://github.com/getmilpa/http)
- `psr/http-message` and `psr/http-factory` — interfaces only. The HTTP projector asks for the PSR-17 factories instead of picking a PSR-7 implementation for you.

Contributing
------------

[](#contributing)

See [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE\_OF\_CONDUCT.md](CODE_OF_CONDUCT.md).

License
-------

[](#license)

Apache-2.0 © Rodrigo Vicente - TeamX Agency. See [LICENSE](LICENSE) and [NOTICE](NOTICE).

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 57.1% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1993784?v=4)[rodrigomx](/maintainers/rodrigomx)[@rodrigomx](https://github.com/rodrigomx)

---

Top Contributors

[![rodrigoteamx](https://avatars.githubusercontent.com/u/269849276?v=4)](https://github.com/rodrigoteamx "rodrigoteamx (8 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (6 commits)")

---

Tags

phpcliframeworkmcpprojectioncommand-patternagent-readymilpasurface-agnostic

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/milpa-console/health.svg)

```
[![Health](https://phpackages.com/badges/milpa-console/health.svg)](https://phpackages.com/packages/milpa-console)
```

###  Alternatives

[utopia-php/cli

A simple CLI library to manage command line applications

42492.7k18](/packages/utopia-php-cli)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
