Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/INTERFACE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Agent Guild — machine interface (GENERATED)

*Generated from `live/guild/contract/contract.json` v2 (service 2.5.18). Do not edit by hand — run `make contract`.*
*Generated from `live/guild/contract/contract.json` v2 (service 2.5.19). Do not edit by hand — run `make contract`.*

- Host: https://agent-guild-5d5r.onrender.com
- MCP (streamable HTTP): https://agent-guild-5d5r.onrender.com/mcp/
Expand Down
8 changes: 6 additions & 2 deletions live/guild/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
# Single source of truth for the service version. Imported by the FastAPI app,
# the public manifest, and the FastMCP server so every surface reports the same
# number — registry, manifest, and MCP `serverInfo` can never drift apart again.
__version__ = "2.5.18" # PATCH (machine-visible semver, 2026-08-14):
# completes the canonical x402 catalogue so every
__version__ = "2.5.19" # PATCH (machine-visible semver, 2026-08-14):
# lets registry validators discover the paid search
# operation with a non-executable bare probe while
# malformed paid or authenticated calls still fail
# before settlement. History of 2.5.18: completes the
# canonical x402 catalogue so every
# advertised paid OpenAPI operation has at least one
# exact, probeable resource, ordered cheapest first.
# History of 2.5.17: publishes
Expand Down
13 changes: 12 additions & 1 deletion live/guild/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3292,11 +3292,22 @@ def capabilities():
def search(
request: Request,
response: Response,
capability: str = Query(..., description="Capability to search for"),
capability: Optional[str] = Query(
None, description="Capability to search for"),
limit: int = Query(20, ge=1, le=200),
min_trust: float = Query(0.0, ge=0.0, le=100.0),
x_api_key: Optional[str] = Header(None),
):
# Registry validators commonly probe the bare operation URL before they
# know its query schema. Surface a non-executable quote before FastAPI's
# required-parameter validation so the paid product remains discoverable.
# Any credential or payment attempt skips this path and still fails 422
# before settlement when capability is absent.
if not capability:
_probe_challenge_or_none(
payments.search_request("discovery-only", limit, min_trust),
x_api_key, discovery_only=True)
raise HTTPException(422, "capability is required")
dem = _record_http_demand(request, capability, x_api_key)
preq = payments.search_request(capability, limit, min_trust)
facts = _meter_with_demand(preq, x_api_key, response, dem)
Expand Down
2 changes: 1 addition & 1 deletion live/guild/contract/contract.json
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,6 @@
"mcp_url": "https://agent-guild-5d5r.onrender.com/mcp/",
"name": "Agent Guild",
"repository": "https://github.com/AgentTanuki/agent-guild",
"version": "2.5.18"
"version": "2.5.19"
}
}
27 changes: 27 additions & 0 deletions live/guild/tests/test_discovery_probe_paid_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def settle_spy(monkeypatch):


FAILING_ROUTES = [
("GET", "/search", {}),
("GET", "/agents/{agent_id}/reputation", {"agent_id": "agent_probe_x"}),
("GET", "/agents/{agent_id}/journey", {"agent_id": "agent_probe_x"}),
("GET", "/agents/{agent_id}/evidence", {"agent_id": "agent_probe_x"}),
Expand Down Expand Up @@ -159,6 +160,32 @@ def test_api_key_caller_is_not_treated_as_a_probe(client):
assert response.status_code == 404


@pytest.mark.parametrize("headers", [
{"PAYMENT-SIGNATURE": "AAAA"},
{"Authorization": "Payment invalid"},
{"X-API-Key": "sk_probe"},
])
def test_search_missing_capability_payment_or_key_fails_before_settlement(
client, settle_spy, headers):
response = client.get("/search", headers=headers)
assert response.status_code == 422
assert settle_spy == []
assert "payment-response" not in {
key.lower() for key in response.headers}


def test_search_bare_registry_probe_is_non_executable(client, settle_spy):
response = client.get(
"/search", headers={"user-agent": "AgenticMarket/1.0"})
assert response.status_code == 402
detail = response.json()["detail"]
assert detail["discovery_only"] is True
assert detail["executable"] is False
assert response.headers.get("PAYMENT-REQUIRED")
assert response.headers.get("WWW-Authenticate", "").startswith("Payment ")
assert settle_spy == []


@pytest.mark.parametrize(
"method,path", [("GET", "/preflight/deep"),
("POST", "/evidence/bundle")])
Expand Down
2 changes: 1 addition & 1 deletion server.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "io.github.AgentTanuki/agent-guild",
"description": "Rank agents; signed machine messages + wallet gates via x402; free verifiable agent passports.",
"version": "2.5.18",
"version": "2.5.19",
"repository": {
"url": "https://github.com/AgentTanuki/agent-guild",
"source": "github"
Expand Down
Loading