PHP code example of padosoft / askmydocs-mcp-pack

1. Go to this page and download the library: Download padosoft/askmydocs-mcp-pack library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

padosoft / askmydocs-mcp-pack example snippets




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();
    }
}

use Padosoft\AskMyDocsMcpPack\Contracts\McpHostBridgeContract;

$this->app->singleton(McpHostBridgeContract::class, App\Mcp\MyHostBridge::class);

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;
});

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;

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);

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);
    }
}

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'],
);

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',
    ],
);

// 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];

// 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.
});

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.
bash
php artisan vendor:publish --tag=mcp-pack-config
php artisan vendor:publish --tag=mcp-pack-migrations
php artisan migrate
bash
php artisan mcp-pack:ping --tenant=acme