agentstatus-mcpjobgraph
Stop paying for out-of-order MCP tool calls.
Agents ignore "call X first" in your tool descriptions. They still hit get_package / fetch / get_deal with garbage IDs. You still eat the traffic, the error noise, and the bad demos.
agentstatus-mcpjobgraph is a thin author-side gate: illegal order never reaches your tools. Legal order still runs, and binds can fill downstream args from earlier steps.
Measured on public MCPs (2026-09-01): 7/7 eligible servers — bare MCP got the illegal call; middleware blocked it with upstream never hit. See results/friction_top9_2026-09-01.md.
Install
pip install agentstatus-mcpjobgraph
# optional adapters
pip install "agentstatus-mcpjobgraph[fastmcp]"
pip install "agentstatus-mcpjobgraph[mcp]"
Python 3.10+. Core has zero required dependencies.
From AgentStatus / Carmel Labs (agentstatus.dev).
60-second FastMCP drop-in
Hand-author the job. Do not ship inferred graphs without reading them.
from fastmcp import FastMCP
from agentstatus_mcpjobgraph import Workflow, install
mcp = FastMCP("packages")
@mcp.tool
def search_packages(query: str) -> dict:
return {"package_id": "abc123", "name": query}
@mcp.tool
def get_package(package_id: str) -> dict:
return {"package_id": package_id, "ok": True}
install(
mcp,
[
Workflow(
id="search_then_get",
steps=["search_packages", "get_package"],
binds={"package_id": "search_packages.package_id"},
)
],
)
if __name__ == "__main__":
mcp.run()
Calling get_package first → blocked with a repair hint. Never hits your function.
Official MCP Python SDK
from mcp.server import Server
from agentstatus_mcpjobgraph import Workflow, make_enforcer, jobgraph_middleware
server = Server("packages")
enforcer = make_enforcer([
Workflow(id="search_then_get", steps=["search_packages", "get_package"],
binds={"package_id": "search_packages.package_id"}),
])
server.middleware.append(jobgraph_middleware(enforcer))
Or wrap a classic handler:
from agentstatus_mcpjobgraph import make_enforcer, Workflow, wrap_call_tool
enforcer = make_enforcer([Workflow(id="x", steps=["a", "b"])])
async def handle(name: str, arguments: dict):
...
handle = wrap_call_tool(enforcer, handle)
In-process decorator (@with_jobs)
from agentstatus_mcpjobgraph import Workflow, make_enforcer, with_jobs
enforcer = make_enforcer([Workflow(id="x", steps=["a", "b"])])
@with_jobs(enforcer, session_id="default")
def call_tool(name: str, arguments: dict):
return dispatch(name, arguments)
Hand-authored workflows (preferred)
from agentstatus_mcpjobgraph import Workflow, load_workflows
workflows = [
Workflow(
id="search_then_get",
steps=["search_packages", "get_package"],
binds={"package_id": "search_packages.package_id"},
)
]
# or
workflows = load_workflows("workflows.json")
workflows.json:
{
"workflows": [
{
"id": "search_then_get",
"steps": ["search_packages", "get_package"],
"binds": {"package_id": "search_packages.package_id"}
}
]
}
Bootstrap only (then edit)
Infer candidates from a live catalog, then rewrite by hand:
from agentstatus_mcpjobgraph import bootstrap_workflows_from_tools
sketches = bootstrap_workflows_from_tools(tools, instructions)
# review / edit sketches → ship as Workflow(...)
Inference is a sketch. Enforcement should run on jobs you own.
HTTP proxy (no code change on the origin)
python -m agentstatus_mcpjobgraph proxy --url https://your-mcp.example/mcp --port 8765
Point clients at http://127.0.0.1:8765/mcp.
Prove friction removal
python -m agentstatus_mcpjobgraph friction --url https://chat.gitsim.com/api/mcp
Expect friction_removed=True: illegal order hits upstream without the gate; blocked with upstream_hit=false with the gate.
What this is / is not
| Is | Is not |
|---|---|
| Author-side enforcement | A Cursor/Claude host patch |
| Order + bind gate | Auth, rate limits, or billing |
| Pip-installable OSS sketch → product | A hosted Carmel service |
Emit-only _meta.workflows without a gate does not remove friction. Hosts ignore it. This package is the gate.
Package layout
agentstatus_mcpjobgraph/
workflow.py # hand-authored Workflow (preferred)
middleware.py # JobEnforcer core
with_jobs.py # @with_jobs + install()
fastmcp_ext.py # FastMCP middleware
mcp_sdk.py # official MCP Python SDK middleware
bootstrap.py # infer once, then edit (not the default)
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentstatus_mcpjobgraph-0.2.0.tar.gz.
File metadata
- Download URL: agentstatus_mcpjobgraph-0.2.0.tar.gz
- Upload date:
- Size: 25.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
21b511403f5d1166e892fd8d1a7e238b5c25d9e20400477f5cae9b5113b8a9d0
|
|
| MD5 |
b02b5da52d8ff4b5fa605c7ef08c1c63
|
|
| BLAKE2b-256 |
f8d0da8fd10d3b60399bae0d22f291ca70d5fe7b1d1c4eb8e686761232f2f5b8
|
File details
Details for the file agentstatus_mcpjobgraph-0.2.0-py3-none-any.whl.
File metadata
- Download URL: agentstatus_mcpjobgraph-0.2.0-py3-none-any.whl
- Upload date:
- Size: 30.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c128cf1859365c2805251ee5cdcf75549eca59523978106b0756837a457cc45
|
|
| MD5 |
731f00604dbdad5b47f9b4eedfa0541c
|
|
| BLAKE2b-256 |
421be799c10c90a8ccd6fbacdad5140a8cf72413cb7c68f353fbe77ffffe161a
|