Erweitere Marktmanagement-MCP fuer Durchfuehrungen
This commit is contained in:
1 parent
df5a533983
commit
3267a121de
3 files changed
+288
No files matched your search
@@ -0,0 +1,216 @@
|
|||||||
|
# Hermes: Market-Management-MCP-API
|
||||||
|
|
||||||
|
Stand: 2026-09-19
|
||||||
|
Status: Aktive technische Schnittstellendokumentation
|
||||||
|
|
||||||
|
## Verbindung
|
||||||
|
|
||||||
|
Hermes verwendet den MCP-Endpunkt der DEV-App:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://erpnaurua.imhochrain.ch/api/market-management-mcp.php
|
||||||
|
```
|
||||||
|
|
||||||
|
Jeder Request ist ein JSON-RPC-2.0-Request über HTTPS. Der Request benötigt:
|
||||||
|
|
||||||
|
```http
|
||||||
|
Authorization: Bearer <MARKET_MCP_TOKEN>
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Token wird nicht in dieser Dokumentation oder im Repository gespeichert.
|
||||||
|
|
||||||
|
## Verbindungsablauf
|
||||||
|
|
||||||
|
### 1. Initialisieren
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "initialize",
|
||||||
|
"params": {
|
||||||
|
"protocolVersion": "2025-03-26"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Werkzeuge laden
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 2,
|
||||||
|
"method": "tools/list",
|
||||||
|
"params": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Werkzeug aufrufen
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 3,
|
||||||
|
"method": "tools/call",
|
||||||
|
"params": {
|
||||||
|
"name": "market_management.list_categories",
|
||||||
|
"arguments": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Ergebnis steht bei `tools/call` als JSON im Textinhalt des ersten
|
||||||
|
`content`-Elements.
|
||||||
|
|
||||||
|
## Verfügbare Werkzeuge
|
||||||
|
|
||||||
|
### Marktdefinitionen
|
||||||
|
|
||||||
|
- `market_management.search_markets`
|
||||||
|
- optional: `search`, `include_archived`, `limit`
|
||||||
|
- `market_management.get_market`
|
||||||
|
- erforderlich: `market_id`
|
||||||
|
- optional: `include_archived`
|
||||||
|
- `market_management.create_market`
|
||||||
|
- erforderlich: `name`, `category_id`
|
||||||
|
- `market_management.update_market`
|
||||||
|
- erforderlich: `market_id`
|
||||||
|
- änderbar: `name`, `category_id`
|
||||||
|
- `market_management.archive_market`
|
||||||
|
- erforderlich: `market_id`
|
||||||
|
- `market_management.reactivate_market`
|
||||||
|
- erforderlich: `market_id`
|
||||||
|
|
||||||
|
### Kategorien
|
||||||
|
|
||||||
|
- `market_management.list_categories`
|
||||||
|
|
||||||
|
Hermes darf Kategorien lesen und bei einem Markt referenzieren, aber keine
|
||||||
|
Kategorien anlegen oder ändern.
|
||||||
|
|
||||||
|
### Durchführungen
|
||||||
|
|
||||||
|
- `market_management.create_occurrence`
|
||||||
|
- erforderlich: `market_definition_id`
|
||||||
|
- initialer Status: `open`
|
||||||
|
- weitere Durchführungsfelder sind optional und werden typisiert validiert
|
||||||
|
- `market_management.get_occurrence`
|
||||||
|
- erforderlich: `occurrence_id`
|
||||||
|
- `market_management.update_occurrence`
|
||||||
|
- erforderlich: `occurrence_id`
|
||||||
|
- `market_management.archive_occurrence`
|
||||||
|
- erforderlich: `occurrence_id`
|
||||||
|
- `market_management.reactivate_occurrence`
|
||||||
|
- erforderlich: `occurrence_id`
|
||||||
|
|
||||||
|
Unterstützte Statuswerte:
|
||||||
|
|
||||||
|
```text
|
||||||
|
open
|
||||||
|
waitlist
|
||||||
|
registered
|
||||||
|
confirmation_pending
|
||||||
|
confirmed
|
||||||
|
cancelled
|
||||||
|
completed
|
||||||
|
```
|
||||||
|
|
||||||
|
Unterstützte Zahlungsstatuswerte:
|
||||||
|
|
||||||
|
```text
|
||||||
|
unpaid
|
||||||
|
paid
|
||||||
|
```
|
||||||
|
|
||||||
|
### Durchführungstage
|
||||||
|
|
||||||
|
- `market_management.create_day`
|
||||||
|
- erforderlich: `occurrence_id`, `date`
|
||||||
|
- `market_management.update_day`
|
||||||
|
- erforderlich: `day_id`
|
||||||
|
- `market_management.delete_day`
|
||||||
|
- erforderlich: `day_id`
|
||||||
|
|
||||||
|
Weitere mögliche Tagesfelder sind `stand_start_time`, `stand_end_time`,
|
||||||
|
`setup_start_time`, `setup_end_time`, `delivery_start_time`,
|
||||||
|
`delivery_end_time` und `observations`.
|
||||||
|
|
||||||
|
### Veranstalter und Kosten
|
||||||
|
|
||||||
|
- `market_management.set_organizer`
|
||||||
|
- erforderlich: `occurrence_id`, `organization_contact_id`,
|
||||||
|
`contact_person_id`
|
||||||
|
- `market_management.create_cost_item`
|
||||||
|
- erforderlich: `occurrence_id`, `cost_type`, `gross_amount`, `tax_status`
|
||||||
|
- `tax_status`: `tax_free` oder `taxable`
|
||||||
|
- `market_management.delete_cost_item`
|
||||||
|
- erforderlich: `cost_item_id`
|
||||||
|
|
||||||
|
## Beispiel: Markt anlegen
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 10,
|
||||||
|
"method": "tools/call",
|
||||||
|
"params": {
|
||||||
|
"name": "market_management.create_market",
|
||||||
|
"arguments": {
|
||||||
|
"name": "Beispielmarkt Hermes Test",
|
||||||
|
"category_id": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Beispiel: Durchführung mit Tag anlegen
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 11,
|
||||||
|
"method": "tools/call",
|
||||||
|
"params": {
|
||||||
|
"name": "market_management.create_occurrence",
|
||||||
|
"arguments": {
|
||||||
|
"market_definition_id": 1,
|
||||||
|
"status": "open",
|
||||||
|
"place": "Beispielort",
|
||||||
|
"registration_deadline": "2026-10-01"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 12,
|
||||||
|
"method": "tools/call",
|
||||||
|
"params": {
|
||||||
|
"name": "market_management.create_day",
|
||||||
|
"arguments": {
|
||||||
|
"occurrence_id": 1,
|
||||||
|
"date": "2026-11-21",
|
||||||
|
"stand_start_time": "09:00",
|
||||||
|
"stand_end_time": "18:00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fachliche Grenzen
|
||||||
|
|
||||||
|
- Hermes erhält keinen direkten Datenbankzugriff.
|
||||||
|
- Hermes darf keine Kategorien anlegen.
|
||||||
|
- Umsatz und Artikelmengen werden nicht über MCP geschrieben; sie werden aus
|
||||||
|
den OTC-Verkäufen abgeleitet.
|
||||||
|
- Markt und Durchführung werden unabhängig archiviert.
|
||||||
|
- Alle Schreibvorgänge werden serverseitig validiert.
|
||||||
|
|
||||||
|
## Fehler
|
||||||
|
|
||||||
|
Bei fehlender oder ungültiger Authentifizierung antwortet der Endpunkt mit
|
||||||
|
HTTP 401. Ungültige JSON-RPC-Requests oder fachlich ungültige Eingaben werden
|
||||||
|
als JSON-RPC-Fehlerantwort zurückgegeben.
|
||||||
@@ -46,6 +46,9 @@ function market_management_mcp_tools(): array
|
|||||||
['name' => 'market_management.create_day', 'description' => 'Durchführungstag anlegen', 'inputSchema' => ['type' => 'object', 'required' => ['occurrence_id', 'date']]],
|
['name' => 'market_management.create_day', 'description' => 'Durchführungstag anlegen', 'inputSchema' => ['type' => 'object', 'required' => ['occurrence_id', 'date']]],
|
||||||
['name' => 'market_management.update_day', 'description' => 'Durchführungstag ändern', 'inputSchema' => ['type' => 'object', 'required' => ['day_id']]],
|
['name' => 'market_management.update_day', 'description' => 'Durchführungstag ändern', 'inputSchema' => ['type' => 'object', 'required' => ['day_id']]],
|
||||||
['name' => 'market_management.delete_day', 'description' => 'Durchführungstag entfernen', 'inputSchema' => ['type' => 'object', 'required' => ['day_id']]],
|
['name' => 'market_management.delete_day', 'description' => 'Durchführungstag entfernen', 'inputSchema' => ['type' => 'object', 'required' => ['day_id']]],
|
||||||
|
['name' => 'market_management.set_organizer', 'description' => 'Veranstalter einer Durchführung setzen', 'inputSchema' => ['type' => 'object', 'required' => ['occurrence_id', 'organization_contact_id', 'contact_person_id']]],
|
||||||
|
['name' => 'market_management.create_cost_item', 'description' => 'Kostenposition anlegen', 'inputSchema' => ['type' => 'object', 'required' => ['occurrence_id', 'cost_type', 'gross_amount', 'tax_status']]],
|
||||||
|
['name' => 'market_management.delete_cost_item', 'description' => 'Kostenposition entfernen', 'inputSchema' => ['type' => 'object', 'required' => ['cost_item_id']]],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +109,12 @@ function market_management_mcp_call(PDO $pdo, string $tool, array $arguments): a
|
|||||||
$pdo,
|
$pdo,
|
||||||
market_management_require_positive_id($arguments['day_id'] ?? null, 'day_id')
|
market_management_require_positive_id($arguments['day_id'] ?? null, 'day_id')
|
||||||
),
|
),
|
||||||
|
'market_management.set_organizer' => market_management_set_organizer($pdo, $arguments),
|
||||||
|
'market_management.create_cost_item' => market_management_create_cost_item($pdo, $arguments),
|
||||||
|
'market_management.delete_cost_item' => market_management_delete_cost_item(
|
||||||
|
$pdo,
|
||||||
|
market_management_require_positive_id($arguments['cost_item_id'] ?? null, 'cost_item_id')
|
||||||
|
),
|
||||||
default => throw new InvalidArgumentException('Unbekanntes MCP-Tool: ' . $tool),
|
default => throw new InvalidArgumentException('Unbekanntes MCP-Tool: ' . $tool),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -442,3 +442,66 @@ function market_management_delete_day(PDO $pdo, int $dayId): array
|
|||||||
}
|
}
|
||||||
return $day;
|
return $day;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function market_management_set_organizer(PDO $pdo, array $input): array
|
||||||
|
{
|
||||||
|
$occurrenceId = market_management_require_positive_id($input['occurrence_id'] ?? null, 'occurrence_id');
|
||||||
|
$organizationId = market_management_require_positive_id($input['organization_contact_id'] ?? null, 'organization_contact_id');
|
||||||
|
$contactPersonId = market_management_require_positive_id($input['contact_person_id'] ?? null, 'contact_person_id');
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
'INSERT INTO market_occurrence_organizer (occurrence_id, organization_contact_id, contact_person_id)
|
||||||
|
VALUES (:occurrence_id, :organization_contact_id, :contact_person_id)
|
||||||
|
ON CONFLICT (occurrence_id) DO UPDATE
|
||||||
|
SET organization_contact_id = EXCLUDED.organization_contact_id,
|
||||||
|
contact_person_id = EXCLUDED.contact_person_id,
|
||||||
|
updated_at = NOW()
|
||||||
|
RETURNING *'
|
||||||
|
);
|
||||||
|
$stmt->execute([
|
||||||
|
':occurrence_id' => $occurrenceId,
|
||||||
|
':organization_contact_id' => $organizationId,
|
||||||
|
':contact_person_id' => $contactPersonId,
|
||||||
|
]);
|
||||||
|
return (array) $stmt->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
function market_management_create_cost_item(PDO $pdo, array $input): array
|
||||||
|
{
|
||||||
|
$occurrenceId = market_management_require_positive_id($input['occurrence_id'] ?? null, 'occurrence_id');
|
||||||
|
$costType = trim((string) ($input['cost_type'] ?? ''));
|
||||||
|
$grossAmount = market_management_optional_number($input, 'gross_amount');
|
||||||
|
$taxStatus = trim((string) ($input['tax_status'] ?? ''));
|
||||||
|
$taxRate = market_management_optional_number($input, 'tax_rate') ?? '0.00';
|
||||||
|
if ($costType === '' || $grossAmount === null || !in_array($taxStatus, ['tax_free', 'taxable'], true)) {
|
||||||
|
throw new InvalidArgumentException('cost_type, gross_amount und tax_status sind erforderlich.');
|
||||||
|
}
|
||||||
|
if (($taxStatus === 'tax_free' && $taxRate !== '0.00') || ($taxStatus === 'taxable' && (float) $taxRate <= 0)) {
|
||||||
|
throw new InvalidArgumentException('tax_rate passt nicht zu tax_status.');
|
||||||
|
}
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
'INSERT INTO market_occurrence_cost_item
|
||||||
|
(occurrence_id, cost_type, description, gross_amount, tax_status, tax_rate)
|
||||||
|
VALUES (:occurrence_id, :cost_type, :description, :gross_amount, :tax_status, :tax_rate)
|
||||||
|
RETURNING *'
|
||||||
|
);
|
||||||
|
$stmt->execute([
|
||||||
|
':occurrence_id' => $occurrenceId,
|
||||||
|
':cost_type' => $costType,
|
||||||
|
':description' => market_management_optional_string($input, 'description'),
|
||||||
|
':gross_amount' => $grossAmount,
|
||||||
|
':tax_status' => $taxStatus,
|
||||||
|
':tax_rate' => $taxRate,
|
||||||
|
]);
|
||||||
|
return (array) $stmt->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
function market_management_delete_cost_item(PDO $pdo, int $costItemId): array
|
||||||
|
{
|
||||||
|
$stmt = $pdo->prepare('DELETE FROM market_occurrence_cost_item WHERE id = :id RETURNING id, occurrence_id');
|
||||||
|
$stmt->execute([':id' => $costItemId]);
|
||||||
|
$item = $stmt->fetch();
|
||||||
|
if (!is_array($item)) {
|
||||||
|
throw new RuntimeException('Kostenposition nicht gefunden.');
|
||||||
|
}
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user