In Ihre erste CLI haben wir dbf gebaut, ein Konsolenwerkzeug in Visual FoxPro, das Tabellen ansieht und exportiert. Heute geben wir dasselbe Werkzeug einem KI-Agenten. Am Ende haben Sie Folgendes: Sie sagen zu Claude „leg mir die Northwind-Kunden in eine CSV“, und Claude findet den Befehl exportar, füllt Tabelle und Ziel aus, führt ihn aus und sagt Ihnen, wie viele Datensätze kopiert wurden. Niemand hat ihm erklärt, was dbf ist oder wie seine Kommandozeile aussieht.
Sie schreiben keine einzige neue Codezeile. Die Befehle, ihre Parameter und ihre Beschreibungen sind die, die Sie schon in die *!*-Blöcke geschrieben haben, und der Code, der läuft, ist dieselbe main.prg mit 76 Zeilen.
Möglich macht das MCP (Model Context Protocol): So fragt ein Agent ein Programm, welche Werkzeuge es hat, und ruft sie auf. Seit FoxForge 0.4.0 bringt jede CLI, die Sie kompilieren, ihren MCP-Server gleich mit.
Was Sie brauchen
- Die CLI
dbfaus dem vorigen Tutorial oder eine andere eigene. - FoxCli und FoxCli MCP, mit FoxForge 0.4.0 oder neuer. Stammt Ihr Projekt aus einem älteren FoxForge, bietet das Kompilieren an, es auf den neuesten Stand zu bringen.
- Die 32-Bit-Laufzeit von .NET 10 auf dem Rechner, auf dem der Agent läuft. Der Installer von FoxCli sucht sie und bietet, falls sie fehlt, den Download bei Microsoft an. Die Konsolen-CLI braucht sie nicht, nur der Server.
- Einen MCP-Client: zum Beispiel Claude Code oder Claude Desktop.
Die Laufzeit muss die 32-Bit-Version (x86) sein. Die 64-Bit-Version taugt nicht, auch wenn sie installiert ist, weil der Server mit Visual FoxPro arbeitet, und das ist 32-Bit. Wenn Sie sie von Hand installiert haben, prüfen Sie, dass Sie „x86“ heruntergeladen haben.
Schritt 1. Kompilieren wie immer
Öffnen Sie das Projekt dbf in der IDE und drücken Sie Strg+F7, wie im Tutorial. In dist\ erscheinen zwei Dateien mehr als beim letzten Mal:
dbf.mcp.exe der MCP-Server von dbf
dbf.mcp.exe.manifestUnd pack.ps1 packt sie ins Zip:
11 ficheros -> C:\a1\tutorial-b\dbf\dbf-dist.zip
Se despliega descomprimiendo: no hace falta registrar nada ni ser administrador.Das sind die 9 von vorher und die zwei des Servers. Wenn Sie die CLI an einen Rechner weitergeben, an dem sie nur aus der Konsole benutzt wird, lässt pack.ps1 -NoMcp die beiden weg, und es sind wieder 9.
Entpacken Sie das Zip dort, wo Sie Ihre Werkzeuge haben. Bei mir liegt es in C:\herramientas\dbf, wie im Tutorial. Der Server verlangt nur, dass er im selben Ordner wie dbf.exe liegt und so heißt wie die CLI mit .mcp.exe dahinter: An diesem Namen erkennt er, welcher CLI er dient.
Schritt 2. Dem Agenten Bescheid sagen
Bei einem MCP-Client meldet man den Server mit einer Zeile an. In Claude Code in einer Datei .mcp.json im Ordner des Projekts, in dem Sie arbeiten werden:
{
"mcpServers": {
"dbf": {
"command": "C:\\herramientas\\dbf\\dbf.mcp.exe"
}
}
}In Claude Desktop kommt dasselbe hinein, "dbf": { "command": ... } innerhalb von "mcpServers", in seiner Konfigurationsdatei (Einstellungen > Entwickler > Konfiguration bearbeiten).
Die Backslashes sind doppelt, weil es JSON ist. Es gibt keine Argumente einzutragen und sonst nichts zu konfigurieren.
Schritt 3. Ihm eine Aufgabe geben
Ich habe Claude Code im Ordner mit der Northwind-Kopie aus dem Tutorial geöffnet (C:\pruebas-cli\northwind). Als Erstes fragt der Agent beim Start den Server, was er kann, und bekommt Ihre drei Befehle, jeden mit Beschreibung und Parametern. So stellt sich exportar vor:
{
"name": "exportar",
"description": "Copia una tabla a un fichero CSV o XLS",
"inputSchema": {
"type": "object",
"properties": {
"formato": {
"type": "string",
"enum": [
"csv",
"xls"
],
"description": "Formato del fichero",
"default": "csv"
},
"tabla": {
"type": "string",
"description": "La tabla (.dbf)"
},
"destino": {
"type": "string",
"description": "El fichero que se crea"
}
},
"required": [
"tabla",
"destino"
]
}
}All das stammt aus Ihrem *!*-Block: Die Beschreibung ist die erste Zeile, formato akzeptiert nur csv oder xls und ist ohne Angabe csv, und Tabelle und Ziel sind Pflicht. Der Agent liest das und weiß damit, wie er den Befehl benutzt.
Ich frage: „Wie viele Kunden stehen in customers.dbf?“. Er ruft info mit der Tabelle customers.dbf auf und bekommt dasselbe, was Sie in der Konsole sehen würden:
Registros: 91
Campos: 11
Modificada: 2004-12-09Ich bitte ihn: „Leg sie in clientes.csv ab“. Er ruft exportar mit tabla = customers.dbf und destino = clientes.csv auf, und die Antwort ist die Meldung, die exportar auf den Fehlerkanal schreibt:
stderr: 91 registros copiados a clientes.csvDie clientes.csv erscheint im Northwind-Ordner, mit den Akzenten an ihrem Platz, genauso wie wenn Sie es selbst aus der Konsole machen.
Wenn etwas nicht klappt, erfährt der Agent es auf demselben Weg wie Sie. Frage ich ihn nach den Informationen zu facturas.dbf, die es nicht gibt, kommt der Aufruf als Fehler markiert zurück, mit der Meldung aus Ihrem Code:
stderr: dbf: no encuentro la tabla facturas.dbfUnd erfindet er ein Format, hält FoxCli ihn auf, bevor Ihr Code erreicht wird, und antwortet mit der Hilfe des Befehls, also mit dem, was der Agent braucht, um sich zu korrigieren:
dbf: '--formato txt' no es válido. Valores: csv, xlsSchritt 4. @readonly: die Befehle, die nur ansehen
info und campos lesen die Tabelle und ändern nichts. exportar schreibt eine Datei. Das kann der Agent nicht wissen, also behandelt er alle drei gleich, und je nachdem, wie Sie ihn eingestellt haben, fragt er Sie jedes Mal um Erlaubnis, wenn er einen davon benutzen will.
Sie sagen es ihm mit einer Zeile im Block jedes Befehls, der nur ansieht:
*!* Cuántos registros y campos tiene una tabla
*!* @readonly
*!* @tcTabla <> La tabla (.dbf)
*!* @example dbf info customers.dbf
PROCEDURE Info(tcTabla AS String)
*!* La estructura de una tabla: nombre, tipo y ancho de cada campo
*!* @readonly
*!* @tcTabla <> La tabla (.dbf)
*!* @example dbf campos customers.dbf --format json
PROCEDURE Campos(tcTabla AS String)Kompilieren Sie mit Strg+F7 neu. Jetzt kommen info und campos mit der Markierung readOnlyHint beim Agenten an, und ein Client, der sie beachtet, lässt sie laufen, ohne Sie jedes Mal zu fragen. exportar bleibt ohne Markierung, und dafür fragt er Sie weiterhin um Erlaubnis. Das wollen Sie auch bei einem Befehl, der auf Ihre Festplatte schreibt.
Setzen Sie sie nur bei Befehlen, die nichts schreiben, denn der Agent verlässt sich auf die Markierung und fragt nicht noch einmal.
Schritt 5. Was sich ändert, wenn das Programm die ganze Sitzung lang läuft
In der Konsole startet jedes Mal, wenn Sie dbf info tippen, ein Prozess, erledigt seine Arbeit und endet. Mit dem Agenten startet der Server einmal zu Beginn der Sitzung und bedient alle Aufrufe, einen nach dem anderen und in der Reihenfolge, in der sie ankommen. Das ändert drei Dinge.
Was ein Befehl offen lässt, schließt FoxCli. exportar öffnet die Tabelle und schließt sie am Ende mit USE. Scheitert aber COPY TO mittendrin (eine volle Festplatte, ein Ziel in einem Ordner ohne Schreibrecht), wird das USE am Ende nie ausgeführt, und die Tabelle bleibt offen. In der Konsole wäre das egal, weil der Prozess endet. Hier fände der nächste Aufruf sie offen vor. Deshalb führt FoxCli nach jedem Aufruf, auch nach denen, die scheitern, AfterCommand aus, eine Methode der Basisklasse, die CLOSE DATABASES ALL und CLEAR MEMORY macht. Sie müssen sie nicht schreiben, sie ist schon da.
Die SETs rührt sie nicht an. Ändert ein Befehl von Ihnen SET DELETED oder SET DATE, gilt die Änderung auch für den nächsten. Wenn nötig, überschreiben Sie AfterCommand in Ihrer Klasse und stellen den alten Zustand wieder her, mit DODEFAULT() am Ende, damit die Tabellen weiter geschlossen werden:
PROCEDURE AfterCommand
SET DELETED ON
DODEFAULT()
ENDPROCAfterCommand erscheint nie als Befehl, obwohl die Methode öffentlich ist.
Der Arbeitsordner ist der des Agenten. Ich habe einfach nach customers.dbf gefragt, ohne Pfad, und er hat die Tabelle gefunden, weil Claude Code im Northwind-Ordner arbeitet. Das ist wie in der Konsole, wo dbf info customers.dbf die Tabelle in dem Ordner sucht, in dem Sie gerade sind. Arbeitet Ihr Client in keinem bestimmten Ordner, verlangen Sie vollständige Pfade.
Niemand beantwortet Fragen. In der Konsole kann ein Befehl anhalten, Sie mit Console.Prompt("¿Sobrescribo el fichero? ") („Datei überschreiben?“) etwas fragen und warten, bis Sie die Antwort tippen. Unter MCP sitzt niemand an der Tastatur, denn über die Eingabe des Programms kommen die Anfragen des Agenten. Ruft ein Befehl von Ihnen Console.Prompt() auf, bekommt er sofort .NULL., als wäre die Eingabe zu Ende, und die Antwort an den Agenten trägt einen Hinweis. dbf fragt nichts, ist also nicht betroffen. Wenn Ihre Befehle fragen, machen Sie diese Angabe zu einem Parameter des Befehls: Den kann der Agent ausfüllen.
Die Lizenz
Der Server hat seine eigene Lizenz, FoxCli MCP: zwei Dateien, foxcli-mcp.lic und foxcli-mcp.seal, die Sie unter FoxForge > FoxCli-Lizenz… laden, genauso wie die von FoxCli. Liegen sie im selben Ordner, werden sie zusammen installiert. Ohne Lizenz funktioniert alles genauso und ohne Ablaufdatum; das Einzige ist, dass am Ende jeder Antwort an den Agenten diese Zeile steht, und dass er sie auch beim Verbinden zu hören bekommt:
Hecho con FoxCli MCP (evaluación) - irwinrodriguez.dev/foxcli-mcpMit geladener Lizenz siegelt Strg+F7 den Server mit Ihrem Namen, und die Zeile verschwindet.
Welches wofür
Ich habe drei Produkte, die MCP sprechen, und jedes hat seinen eigenen Zweck:
- FoxAgent verbindet einen Agenten mit einer geöffneten VFP-Anwendung: Er sieht ihre Formulare, ihre Daten und ihren Zustand, während Sie sie benutzen.
- FoxCli MCP ist das von heute: Ihre VFP-Logik, als Werkzeuge verpackt, auf demselben Rechner wie der Agent.
- FoxServer stellt Ihre VFP-Logik auf einen Server, damit sie von einem anderen Rechner aus aufgerufen werden kann.
Wie es weitergeht
- Die FoxCli-Anleitung unter FoxForge > Hilfe > FoxCli-Anleitung hat ein Kapitel zum MCP-Server mit den Einzelheiten, die hier nicht vorkamen.
- Wenn etwas nicht startet, setzen Sie
"env": { "FOXCLI_MCP_TRACE": "1" }neben das"command"in der Konfiguration: Der Server berichtet dann auf dem Fehlerkanal, was er gerade tut, und Ihr MCP-Client zeigt diesen Kanal meist in seinem Protokoll. - Eine Idee, wie es mit
dbfweitergeht: Der Befehlcontarmit Filter, den ich im vorigen Tutorial vorgeschlagen habe, ist ein guter Kandidat für@readonly. Damit können Sie Claude fragen, wie viele Kunden es in Spanien gibt, ohne ihm für irgendetwas anderes die Erlaubnis zu geben.