padosoft / askmydocs-mcp-pack
Framework-agnostic MCP (Model Context Protocol) plumbing for Laravel β contracts, multi-turn tool-calling orchestrator, stdio client bridge, audit trail, RBAC hooks. Powers AskMyDocs and reusable in any Laravel AI app.
Requires
- php: ^8.3
- ext-json: *
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/events: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- symfony/process: ^7.0|^8.0
Requires (Dev)
- mockery/mockery: ^1.6
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpunit/phpunit: ^10.5|^11.0|^12.0
- dev-main
- v1.4.0
- v1.3.0
- v1.2.0
- v1.1.0
- v1.0.1
- v1.0.0
- dev-imgbot
- dev-feature/v1.4.0-admin-rest-backend
- dev-feature/v1.3.0-circuit-breaker-retry
- dev-feature/v1.2.0-server-side
- dev-feature/v1.1.0-sse-resources-prompts
- dev-docs/v1-roadmap-before-host-integration
- dev-feature/v1.0.1-migration-hastable-guard
This package is auto-updated.
Last update: 2026-05-16 01:12:18 UTC
README
Framework-agnostic Model Context Protocol plumbing for Laravel. Contracts, multi-turn tool-calling orchestrator, stdio + HTTP transports, audit trail, RBAC hooks. Powers AskMyDocs and reusable in any Laravel AI app.
π AI vibe-coding pack included
Every Padosoft package ships a .claude/ folder with curated skills, rules,
and commands so Claude Code, Cursor, Copilot, and any other LLM agent
can drive the package productively from day one. The pack documents the
extension points the framework guarantees as stable (contracts,
events, config keys) and the ones that are intentionally private β
so AI agents stop guessing and start composing.
# Drop into a fresh consumer project composer require padosoft/askmydocs-mcp-pack cp -r vendor/padosoft/askmydocs-mcp-pack/.claude ./ # Then ask Claude Code: "wire the orchestrator into my host bridge"
Table of contents
- Why this package?
- Features at a glance
- Screenshots β Admin Web Panel
- Comparison vs alternatives
- Installation
- Quick start (3 minutes)
- Architecture
- Core concepts
- Configuration reference
- Recipes
- Extension points
- Testing
- Compatibility matrix
- Roadmap
- Changelog
- License
Why this package?
MCP is the open standard Anthropic released in November 2024 for LLM β tool wire-format interoperability. Within months it was adopted by Cursor, Claude Desktop, VS Code, Cline, Continue, Sourcegraph Cody, OpenAI's Realtime API, and a long tail of editor extensions and agentic frameworks.
What MCP gives you:
- A JSON-RPC 2.0 contract for
initialize,tools/list,tools/call,resources/*,prompts/*. - Transport choice β stdio (child process) for desktop tools and HTTP/SSE for cloud gateways.
- A growing public catalog of servers (filesystem, GitHub, Slack, Postgres, Notion, Sentry, β¦) you can plug into any client.
What MCP does not give you (and what this pack adds):
- A multi-turn tool-calling loop that drives the model β tools β model β tools cycle with budget caps and audit trail.
- RBAC / tenant gates in front of every tool invocation.
- An opinionated audit table with SHA-256 input/output hashes, duration, status, and error excerpts β the kind of trail an EU AI-Act audit will ask for.
- Provider-agnostic integration: the orchestrator does NOT bind
the OpenAI / Anthropic / Gemini SDK. You implement a 30-line
McpHostBridgeContractagainst your existing chat manager, and the pack handles the rest.
That is exactly the shape AskMyDocs needed for v7.0. We extracted it so the next Laravel AI app does not have to reinvent it.
Features at a glance
| β | Capability |
|---|---|
| π | Two transports out of the box β stdio (Symfony Process) and http (Guzzle via Laravel HTTP client). |
| π§ | Multi-turn tool-calling orchestrator β bounded by max_iterations, with deterministic message reshaping. |
| π‘οΈ | Tenant-scoped tool catalog β forTenant($id) filters by tenant; cross-tenant leakage is structurally impossible. |
| π¦ | Per-call RBAC β McpToolAuthorizerContract gates every tool BEFORE it appears in the catalog. |
| π§Ύ | Hash-only audit trail β mcp_tool_call_audit rows store SHA-256 of input + result, NOT raw payloads. |
| π | Cached handshakes β initialize + tools/list are cached per (tenant, server) for 5 min by default. |
| π§ͺ | Stub-friendly tests β McpClient::useTransportResolver() swaps the transport with a one-line closure. |
| π¦ | Zero-AI-SDK lock-in β pluggable host bridge; works with any provider. |
| π | Production telemetry β every tool call carries duration_ms, status, and error excerpt. |
| π§° | Artisan diagnostics β php artisan mcp-pack:ping walks the registry and prints a per-server status table. |
Screenshots β Admin Web Panel
The companion SPA (padosoft/askmydocs-mcp-pack-admin,
post-v7.0 cycle) consumes the v1.4 admin REST routes shipped here. Light + dark
themes ship out of the box; every action is keyboard-reachable and
audit-logged.
Dashboard
The landing surface β fleet health, breaker open-count, recent audit volume per server, and at-a-glance latency percentiles.
| Light | Dark |
|---|---|
![]() |
![]() |
Servers + per-server detail + tools + circuit breakers
Servers list filters by tenant + transport + status. Drill in for the
handshake-cached tool catalog, the most recent audit slice, and the
breaker state for every (server, tool) pair β read from
CircuitBreaker::peekState() so the dashboard NEVER consumes the
half-open probe slot just by polling.
Audit + audit detail + per-server audit
Paginated audit query over the configurable mcp-pack.audit_model,
tenant-scoped by default. Filters: server_id, tool_name,
status, date range. Click a row for the SHA-256 input/output
hashes + redacted error excerpt + duration.
Prompts + API playground + Settings
Prompt catalog (JSON-RPC prompts/list / prompts/get), an
embedded API playground for verifying the routes against the host's
auth middleware, and a settings surface mirroring the
mcp-pack.* config block.
Comparison vs alternatives
| Feature | askmydocs-mcp-pack | laravel/mcp (Laravel first-party) |
php-llm/mcp-sdk (community) |
Roll-your-own |
|---|---|---|---|---|
| MCP client support (call upstream) | β stdio + http | β server-only | β stdio + http | DIY |
| MCP server support (expose tools) | β οΈ via host | β | β | DIY |
| Multi-turn tool-calling loop | β | β | β | DIY (~300 LOC) |
| Provider-agnostic host bridge | β | n/a | β (OpenAI-coupled) | DIY |
| Tenant boundary built-in | β
forTenant($id) |
β | β | DIY |
| Audit trail with hashes | β migration shipped | β | β | DIY (~ADR + migration) |
| RBAC hook before tool exposure | β contract | β | β | DIY (middleware?) |
| Cached handshake | β 5min default | β | β | DIY |
| Stub transport for tests | β one-line closure | β | partial | DIY |
| .claude/ vibe-coding pack | β | β | β | DIY |
| License | MIT | MIT | MIT | n/a |
laravel/mcp is excellent for exposing Laravel as an MCP server β
this pack and laravel/mcp are complementary, not competing. Use both
together: laravel/mcp to expose your KB as a server, and this pack
to consume other MCP servers from inside your chat flow.
Installation
composer require padosoft/askmydocs-mcp-pack
Publish config + migrations (optional β both load automatically):
php artisan vendor:publish --tag=mcp-pack-config php artisan vendor:publish --tag=mcp-pack-migrations php artisan migrate
Service provider is auto-discovered via composer.json::extra.laravel.providers.
Quick start (3 minutes)
1. Implement the host bridge
This is the one piece you must write β about 30 lines of glue against your existing chat provider:
<?php namespace App\Mcp; use App\Ai\AiManager; use Padosoft\AskMyDocsMcpPack\Contracts\McpHostBridgeContract; use Padosoft\AskMyDocsMcpPack\Support\HostChatResponse; use Padosoft\AskMyDocsMcpPack\Support\HostChatTurn; final class MyHostBridge implements McpHostBridgeContract { public function __construct(private readonly AiManager $ai) {} public function chat(HostChatTurn $turn): HostChatResponse { // Translate $turn->tools into your provider's tool-calling shape. $providerTools = array_map( fn($tool) => [ 'type' => 'function', 'function' => [ 'name' => $tool->name(), 'description' => $tool->description(), 'parameters' => $tool->schema(), ], ], $turn->tools, ); $response = $this->ai->chatWithHistory('', $turn->messages, [ 'tools' => $providerTools, 'tool_choice' => 'auto', ] + $turn->extras); return new HostChatResponse( content: $response->content, toolCalls: $this->normalizeToolCalls($response->toolCalls), provider: $response->provider, model: $response->model, ); } public function supportsToolCalling(): bool { return in_array($this->ai->provider()->name(), ['openai', 'openrouter'], true); } private function normalizeToolCalls(?array $raw): array { return collect($raw ?? [])->map(fn($c) => [ 'id' => $c['id'], 'name' => $c['function']['name'] ?? $c['name'], 'arguments' => is_string($c['function']['arguments'] ?? '') ? json_decode($c['function']['arguments'], true) ?? [] : ($c['arguments'] ?? []), ])->all(); } }
2. Bind it in AppServiceProvider
use Padosoft\AskMyDocsMcpPack\Contracts\McpHostBridgeContract; $this->app->singleton(McpHostBridgeContract::class, App\Mcp\MyHostBridge::class);
3. Register at least one MCP server
In-memory (config-driven):
use Padosoft\AskMyDocsMcpPack\Contracts\McpServerRegistryContract; use Padosoft\AskMyDocsMcpPack\Defaults\InMemoryMcpServer; use Padosoft\AskMyDocsMcpPack\Defaults\InMemoryMcpServerRegistry; $this->app->singleton(McpServerRegistryContract::class, function () { $registry = new InMemoryMcpServerRegistry(); $registry->add(new InMemoryMcpServer( id: 'fs', name: 'Filesystem', transport: 'stdio', tenantId: null, // platform-global transportConfig: [ 'command' => 'npx', 'args' => ['-y', '@modelcontextprotocol/server-filesystem', '/data'], 'timeout_ms' => 10_000, ], allowedTools: ['read_file', 'list_directory'], )); return $registry; });
Or back it with your own Eloquent model β see Recipes.
4. Drive a chat turn
use Padosoft\AskMyDocsMcpPack\Services\McpToolCallingService; use Padosoft\AskMyDocsMcpPack\Support\HostMessage; $svc = app(McpToolCallingService::class); $response = $svc->chatWithTools( messages: [ HostMessage::system('You are AskMyDocs. Use tools when grounded retrieval helps.'), HostMessage::user('What did the deploy runbook change in March?'), ], tenantId: 'acme', actor: auth()->id(), context: ['conversation_id' => 42, 'message_id' => 7], ); return $response->content;
Behind the scenes the orchestrator:
- Looks up enabled servers for tenant
acme. - Handshakes each one (cached for 5 min).
- Filters tools through your
McpToolAuthorizerContract. - Hands the catalog to your
MyHostBridge::chat(). - If the model asks for a tool: invokes it through
tools/call, appends the result, and loops back. - Audits every call into
mcp_tool_call_audit.
5. Sanity-check
php artisan mcp-pack:ping --tenant=acme
+-----+------------+-----------+--------+--------+---------+-------+
| id | name | transport | tenant | status | #tools | error |
+-----+------------+-----------+--------+--------+---------+-------+
| fs | Filesystem | stdio | acme | ok | 11 | |
+-----+------------+-----------+--------+--------+---------+-------+
Architecture
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your controller β
β βββΊ McpToolCallingService::chatWithTools() β
β β β
β βββΊ McpServerRegistryContract::forTenant($id) βββ tenant gate β
β βββΊ McpHandshakeService::refresh() βββ cached β
β βββΊ McpToolAuthorizerContract::authorize() βββ RBAC gate β
β β β
β βββΊ McpHostBridgeContract::chat($turn) βββ YOUR CODE β
β β (turn = messages + tool catalog + tenant + extras) β
β β β
β βββΊ (loop until model returns no tool_calls or budget hits) β
β β β
β βββΊ ToolInvoker::invoke() β
β β βββΊ McpClient::callTool() ββ JSON-RPC tools/call βββββ β
β β βββΊ McpToolCallAudit::create() βββ audit row β
β β β
β βββΊ returns HostChatResponse(content, toolCalls, usage) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββΌβββββββββββββββββ
β Upstream MCP server β
β (stdio child process OR β
β HTTP gateway) β
βββββββββββββββββββββββββββββββββββ
Five contracts, three transports, one orchestrator. The blast-radius of swapping any one of them is bounded by the contract.
Core concepts
McpServerContract
A single MCP endpoint your host can talk to. Carries:
id()β stable identifier scoped per tenant.transport()βstdioorhttp.tenantId()βnull= platform-global; a string = scoped to that tenant.transportConfig()β{command, args, cwd, env}for stdio;{endpoint, headers, timeout_ms}for http.allowedTools()β empty array = "all tools the server advertises"; otherwise a per-server allow-list.
Default implementation: InMemoryMcpServer. Production: subclass it
on top of your Eloquent model.
McpServerRegistryContract
Per-tenant catalog of McpServerContract entries. The orchestrator
always asks forTenant($id) β never a global all(). Cross-tenant
leakage is structurally impossible.
Default implementation: InMemoryMcpServerRegistry. Production: back
it with your own McpServer Eloquent model.
McpHostBridgeContract
The 30-line wrapper around your existing chat manager (OpenAI, Anthropic, OpenRouter, Gemini, β¦). The pack does NOT bind any AI SDK β this is what keeps it provider-agnostic.
McpToolAuthorizerContract
RBAC gate. Called BEFORE the tool appears in the model's catalog, so denied tools never even reach the prompt token budget.
Default implementation: NullMcpToolAuthorizer (allows everything β
fine for prototypes, MUST be replaced in production).
McpToolContract
The unit of work. Most consumers don't implement this directly β
RemoteMcpTool is built from the upstream server's tools/list
response and used by the orchestrator. You implement it only if you
need to expose an in-process tool with no upstream MCP server
(uncommon).
Configuration reference
config/mcp-pack.php:
| Key | Env var | Default | Purpose |
|---|---|---|---|
tool_calling.enabled |
MCP_PACK_TOOL_CALLING_ENABLED |
false |
Master kill-switch. |
tool_calling.max_iterations |
MCP_PACK_TOOL_CALLING_MAX_ITERATIONS |
3 |
Hard cap on tool-calling loops per chat turn. |
tool_calling.default_tool_choice |
MCP_PACK_TOOL_CHOICE |
auto |
OpenAI-style hint passed to the bridge. |
handshake.ttl_seconds |
MCP_PACK_HANDSHAKE_TTL |
300 |
How long to cache initialize + tools/list. |
audit_model |
MCP_PACK_AUDIT_MODEL |
McpToolCallAudit::class |
Override to subclass the audit model. |
Recipes
Recipe 1 β back the registry with an Eloquent model
final class EloquentMcpServerRegistry implements McpServerRegistryContract { public function forTenant(?string $tenantId): array { return McpServer::query() ->where('tenant_id', $tenantId) ->where('enabled', true) ->get() ->map(fn($m) => new InMemoryMcpServer( id: (string) $m->id, name: $m->name, transport: $m->transport, tenantId: $m->tenant_id, transportConfig: $m->transport_config ?? [], allowedTools: $m->allowed_tools ?? [], )) ->all(); } public function find(string $id): ?McpServerContract { $m = McpServer::query()->where('id', $id)->where('enabled', true)->first(); return $m === null ? null : new InMemoryMcpServer(/* same wrap as above */); } } $this->app->singleton(McpServerRegistryContract::class, EloquentMcpServerRegistry::class);
Recipe 2 β Spatie-permission-backed authorizer
final class SpatieMcpToolAuthorizer implements McpToolAuthorizerContract { public function authorize(mixed $actor, ?string $tenantId, McpToolContract $tool): bool { if (! $actor instanceof User) { return false; } if (! $actor->hasAnyRole(['admin', 'super-admin'])) { return false; } $permission = $tool->isReadOnly() ? "mcp.{$tool->name()}.read" : "mcp.{$tool->name()}.write"; return $actor->hasPermissionTo($permission); } }
Recipe 3 β Claude Desktop / Cursor MCP server over stdio
new InMemoryMcpServer( id: 'github', name: 'GitHub MCP', transport: 'stdio', tenantId: 'acme', transportConfig: [ 'command' => 'npx', 'args' => ['-y', '@modelcontextprotocol/server-github'], 'env' => ['GITHUB_PERSONAL_ACCESS_TOKEN' => env('GH_PAT')], 'timeout_ms' => 15_000, ], allowedTools: ['search_repositories', 'get_file_contents'], );
Recipe 4 β remote MCP gateway over HTTPS
new InMemoryMcpServer( id: 'cloud-kb', name: 'Cloud KB Gateway', transport: 'http', tenantId: 'acme', transportConfig: [ 'endpoint' => 'https://mcp.example.com/rpc', 'headers' => ['Authorization' => 'Bearer ' . env('MCP_TOKEN')], 'timeout_ms' => 5_000, 'health_path' => '/healthz', ], );
Recipe 5 β coexist with a host-owned audit table
If your host already owns a mcp_tool_call_audit table that pre-dates
this pack, the package migration is a no-op
(Schema::hasTable('mcp_tool_call_audit') guards both up() and
down()). To keep the host's operator-forensics columns (raw redacted
payload, user-FK, error blob, β¦) AND satisfy the package contract,
ship ONE additive host migration and one model subclass:
// database/migrations/...add_input_hash_and_actor_to_mcp_tool_call_audit.php Schema::table('mcp_tool_call_audit', function (Blueprint $table) { $table->char('input_hash', 64)->nullable()->after('input_json_redacted'); $table->string('actor', 100)->nullable()->after('user_id'); // (also relax any NOT NULL host columns the package does not write) }); // Backfill existing rows so SHA-256 lookups match pre- and post-pack: DB::table('mcp_tool_call_audit') ->whereNull('input_hash') ->orderBy('id') ->chunkById(500, function ($rows) { foreach ($rows as $row) { $payload = is_array($row->input_json_redacted) ? json_encode($row->input_json_redacted, JSON_UNESCAPED_UNICODE) : $row->input_json_redacted; DB::table('mcp_tool_call_audit') ->where('id', $row->id) ->update(['input_hash' => hash('sha256', (string) $payload)]); } });
// app/Models/McpToolCallAudit.php β subclass + bridging hook class McpToolCallAudit extends \Padosoft\AskMyDocsMcpPack\Models\McpToolCallAudit { protected $table = 'mcp_tool_call_audit'; protected $fillable = [ // package contract 'tenant_id', 'actor', 'mcp_server_id', 'tool_name', 'input_hash', 'result_hash', 'duration_ms', 'status', 'error_excerpt', // host-legacy columns kept for admin SPA 'user_id', 'input_json_redacted', 'error_json', ]; protected static function booted(): void { static::creating(function (self $row) { // Bridge actorβuser_id so legacy joins still work. if ($row->user_id === null && is_string($row->actor) && ctype_digit($row->actor)) { $row->user_id = (int) $row->actor; } if (($row->actor === null || $row->actor === '') && $row->user_id !== null) { $row->actor = (string) $row->user_id; } }); } }
// config/mcp-pack.php β point the package at the host subclass return ['audit_model' => \App\Models\McpToolCallAudit::class];
Now every package ToolInvoker::audit() row fills BOTH schemas; legacy
host writes continue to work; the host's existing admin UI and
operator-forensics queries keep rendering the same way they always did.
Recipe 6 β fail fast under upstream outage (v1.3.0)
Drop in the circuit breaker + retry budget so a flaky MCP server doesn't pin every worker on a long timeout:
# .env β opt in, both layers are independent MCP_PACK_CB_ENABLED=true MCP_PACK_CB_FAILURE_THRESHOLD=5 MCP_PACK_CB_RECOVERY_SECONDS=30 MCP_PACK_RETRY_ENABLED=true MCP_PACK_RETRY_MAX_ATTEMPTS=3 MCP_PACK_RETRY_BUCKET_SIZE=20 MCP_PACK_RETRY_BUCKET_WINDOW_SECONDS=60 MCP_PACK_RETRY_BASE_BACKOFF_MS=200 MCP_PACK_RETRY_MAX_BACKOFF_MS=5000
// app/Providers/AppServiceProvider.php β wire alerting to the events use Illuminate\Support\Facades\Event; use Padosoft\AskMyDocsMcpPack\Resilience\Events\CircuitOpened; use Padosoft\AskMyDocsMcpPack\Resilience\Events\RetryExhausted; Event::listen(CircuitOpened::class, function (CircuitOpened $e): void { // Page on-call: a server's tool is failing fast. }); Event::listen(RetryExhausted::class, function (RetryExhausted $e): void { // Dashboard tile: which (tenant, server) is burning its budget. });
ToolInvoker automatically routes through the mediator when either
knob is enabled; consumers don't change a line of code. The breaker
state is per (server_id, tool_name); the budget is per
(tenant_id, server_id) so cross-tenant isolation (R30) holds even
under load.
Extension points
| Hook | Default | Override when⦠|
|---|---|---|
McpHostBridgeContract |
NullMcpHostBridge (throws) |
Always β wire your provider stack. |
McpServerRegistryContract |
InMemoryMcpServerRegistry |
You want DB-backed admin UI for server CRUD. |
McpToolAuthorizerContract |
NullMcpToolAuthorizer (allow-all) |
Always in production β wire RBAC + tenant policy. |
McpToolCallingService |
Bound via SP | Subclass for custom logging / retry / circuit-breaker logic. |
McpHandshakeService |
Bound via SP | Subclass to persist handshakes in a DB column. |
McpToolCallAudit |
Built-in model | Subclass + override mcp-pack.audit_model config. |
McpClient::useTransportResolver() |
null (uses transport from server) |
In tests β swap to a stub transport. |
Testing
The pack ships its own PHPUnit + Orchestra Testbench setup. To run its tests:
composer install vendor/bin/phpunit
To test your own host using the pack's stubs:
use Padosoft\AskMyDocsMcpPack\Services\McpClient; use Padosoft\AskMyDocsMcpPack\Tests\Support\StubMcpTransport; $transport = (new StubMcpTransport()) ->scriptInitialize() ->scriptListTools([['name' => 'kb_search', 'description' => '...', 'inputSchema' => []]]) ->scriptToolCall('kb_search', ['hits' => [['title' => 'Doc A']]]); McpClient::useTransportResolver(fn() => $transport); // drive your chat flow β every JSON-RPC call hits the stub.
End-to-end Playwright coverage in AskMyDocs exercises:
- chat UI with MCP tools enabled β tool-call summary card renders
- admin SPA
/admin/mcp-serversβ server CRUD +pingaction - audit log shows tool calls with status, duration, server name
Compatibility matrix
| PHP | Laravel | Status |
|---|---|---|
| 8.3 | 11.x | β tested in CI |
| 8.3 | 12.x | β tested in CI |
| 8.3 | 13.x | β tested in CI |
| 8.4 | 11.x | β tested in CI |
| 8.4 | 12.x | β tested in CI |
| 8.4 | 13.x | β tested in CI |
| 8.5 | 13.x | β tested in CI |
Roadmap
| Version | Status | Highlights |
|---|---|---|
| v1.0.0 | β shipped 2026-05-15 | Contracts + orchestrator + stdio/http transports + audit + ping. |
| v1.0.1 | β shipped 2026-05-15 | Defensive up()/down() guards on the audit-table migration so the package coexists with a host-owned mcp_tool_call_audit. Recipe 5 walks the coexistence pattern. |
| v1.1.0 | β shipped 2026-05-15 | SseJsonRpcTransport for remote HTTP+SSE gateways; JSON-RPC resources/list + resources/read; JSON-RPC prompts/list + prompts/get. |
| v1.2.0 | β shipped 2026-05-15 | First-class server-side β same package exposes a Laravel app AS an MCP server (stdio long-lived runner + HTTP route + JsonRpcRequestHandler dispatching initialize / tools/list / tools/call / resources/* / prompts/* to a host-supplied catalog + auth + RBAC). |
| v1.3.0 | β shipped 2026-05-15 | Per-tool circuit breaker (closed / open / half_open with TTL recovery) + adaptive retry budget (token-bucket per (tenant, server) with exponential backoff capped at maxBackoffMs) + 5 telemetry events. Opt-in, default OFF. |
| v1.4.0 | β shipped 2026-05-15 | Admin REST backend β read-mostly routes at /api/admin/mcp-pack/{servers,audit,circuit-breaker} registered by the package SP behind MCP_PACK_ADMIN_ENABLED=true. Middleware-driven auth (host wires Sanctum + RBAC). NO React/Vue code β this is the backend the separate padosoft/askmydocs-mcp-pack-admin SPA consumes. CRUD writes deferred to v1.5.0 with a writable registry contract. |
| β | β | β |
| post-v7.0 cycle | π separate package | padosoft/askmydocs-mcp-pack-admin β standalone React SPA companion. Same pattern as padosoft/laravel-flow-admin / padosoft/laravel-pii-redactor-admin. Cross-mountable under /admin/mcp/ in any Laravel host that depends on this package + v1.4. Ships in its own repo with its own R36 cycle once AskMyDocs's v7.0/W6 host integration is green. |
The v1.1 β v1.4 cycle ships before the AskMyDocs host adopts the package. Consumers willing to ride v1.0 today are welcome to do so β the public API surface is stable and won't break before v2 β but AskMyDocs's own host integration is intentionally deferred to land over the complete v1.4 feature set (orchestrator + transports + server-side + circuit breaker + admin REST routes) in a single integration cycle rather than four partial passes. See lopadova/AskMyDocs roadmap for the host-side milestones (v7.0/W2 β W6).
Changelog
v1.0.0 β planned
- Initial release extracted from AskMyDocs v6.x.
McpToolContract,McpServerContract,McpServerRegistryContract,McpToolAuthorizerContract,McpHostBridgeContract.HttpJsonRpcTransport+StdioJsonRpcTransport.McpToolCallingServicemulti-turn loop with budget cap.McpHandshakeServicewith cached handshakes.ToolInvokerwith SHA-256 audit trail.mcp_tool_call_auditmigration.mcp-pack:pingArtisan diagnostic.NullMcpHostBridge+NullMcpToolAuthorizer+InMemoryMcpServerRegistrydefaults.
License
MIT Β© Padosoft. See LICENSE.












