# Migration Guide: 3.0 — UseCase GraphQL MCP

This guide walks through migrating from the **removed** direct-call UseCase MCP entries
(`create_use_case_mcp_server`, `create_use_case_flat_server`) to the new
**GraphQL-based** MCP entry (`create_use_case_graphql_mcp_server`).

## What changed

| Removed (2.x) | Replacement (3.0) |
|---------------|-------------------|
| `create_use_case_mcp_server` | `create_use_case_graphql_mcp_server` |
| `create_use_case_flat_server` | `create_use_case_graphql_mcp_server` |

The old MCP exposed service methods via **direct Python calls** with JSON-encoded
parameters. The new MCP exposes them through a **real GraphQL schema** generated
from `UseCaseService` signatures, with Layer 3 accepting standard GraphQL query
strings.

**Unchanged** (no migration needed):
- `UseCaseService`, `BusinessMeta`, `@query`, `@mutation`, `FromContext`,
  `UseCaseAppConfig` — service declaration is identical.
- `create_use_case_router` (FastAPI REST) — orthogonal surface, untouched.
- `create_jsonrpc_router` (JSON-RPC) — orthogonal surface, untouched.
- `create_use_case_voyager` (visualization) — orthogonal surface, untouched.

## Migrating from `create_use_case_mcp_server`

### Before (2.x)

```python
from nexusx import UseCaseAppConfig, create_use_case_mcp_server

mcp = create_use_case_mcp_server(
    apps=[
        UseCaseAppConfig(
            name="project",
            services=[UserService, TaskService, SprintService],
            description="Project management",
        ),
    ],
    name="Project MCP",
)
mcp.run()
```

LLM agents consumed this via 4 progressive-disclosure tools:
- `list_apps()` — discover apps
- `list_services(app_name)` — list services & method names
- `describe_service(app_name, service_name)` — method signatures
- `call_use_case(app_name, service_name, method_name, params='{"arg": "val"}')` — execute

### After (3.0)

```python
from nexusx import UseCaseAppConfig, create_use_case_graphql_mcp_server

mcp = create_use_case_graphql_mcp_server(
    apps=[
        UseCaseAppConfig(
            name="project",
            services=[UserService, TaskService, SprintService],
            description="Project management",
        ),
    ],
    name="Project MCP",
)
mcp.run()
```

LLM agents now consume this via 4 progressive-disclosure tools:
- `list_apps()` — discover apps (same)
- `describe_compose_schema(app_name)` — list services & method names (renamed)
- `describe_compose_method(app_name, service_name, method_name)` — method signature
  + SDL fragment (renamed)
- `compose_query(app_name, query)` — **execute via GraphQL string** (replaces
  `call_use_case`)

The same `UserService` / `TaskService` / `SprintService` classes work unchanged.

### Calling a method

Before — `call_use_case` with JSON params:

```python
result = await call_use_case(
    app_name="project",
    service_name="TaskService",
    method_name="get_tasks_by_sprint",
    params='{"sprint_id": 42}',
    selection="{ id title owner { name } }",  # optional rootless selection
)
```

After — `compose_query` with GraphQL:

```python
result = await compose_query(
    app_name="project",
    query="""
        query {
          TaskService {
            get_tasks_by_sprint(sprint_id: 42) {
              id
              title
              owner { name }
            }
          }
        }
    """,
)
# result = {"data": {"TaskService": {"get_tasks_by_sprint": [...]}}}
```

### Response envelope

Before — all 4 tools returned `{success, data, hint}` or `{success, error, error_type}`.

After — **split**:
- Layers 0–2 (`list_apps` / `describe_compose_schema` / `describe_compose_method`)
  still return `{success, data}` / `{success, error, error_type}`.
- Layer 3 (`compose_query`) returns **GraphQL standard** `{data, errors}` to match
  what GraphQL clients and AI agents expect from a GraphQL execution surface.

### Introspection is rejected in `compose_query`

The old MCP didn't have this concept. The new `compose_query` rejects queries that
touch `__schema`, `__type`, or `__typename`, returning:

```json
{
  "data": null,
  "errors": [{
    "message": "GraphQL introspection is not available via compose_query. Use describe_compose_schema(app_name=...) and describe_compose_method(...) to discover the schema."
  }]
}
```

Use `describe_compose_schema` (Layer 1) for service/method overview and
`describe_compose_method` (Layer 2) for full SDL fragments.

## Migrating from `create_use_case_flat_server`

The flat MCP exposed one tool per method (`{ServiceName}_{method_name}`), each taking
JSON params. The 3.0 release does not provide a direct flat replacement; the
recommended migration path is to use the GraphQL MCP.

### Before (2.x)

```python
from nexusx import create_flat_mcp_server  # renamed in 2.9.0 to create_use_case_flat_server

mcp = create_use_case_flat_server(
    apps=[UseCaseAppConfig(name="order", services=[OrderService])],
)
mcp.run()
# Tools: OrderService_create_order, OrderService_get_order, ...
```

### After (3.0)

```python
from nexusx import create_use_case_graphql_mcp_server

mcp = create_use_case_graphql_mcp_server(
    apps=[UseCaseAppConfig(name="order", services=[OrderService])],
)
mcp.run()
# Single compose_query tool; agents call methods via GraphQL:
#   query { OrderService { create_order(...) { id } } }
```

If you specifically need a flat tool-per-method surface (e.g., for clients that
can't compose GraphQL strings), build it yourself on top of `build_compose_schema`:

```python
from nexusx import build_compose_schema, UseCaseAppConfig
from fastmcp import FastMCP

app_config = UseCaseAppConfig(name="order", services=[OrderService])
schema = build_compose_schema(app_config)
mcp = FastMCP("Custom Flat MCP")

# Iterate schema's services/methods and register one tool per method.
# (Implementation left to the user — see ComposeSchema.render_method_sdl for SDL.)
```

## FAQ

### Do I need to change my `UseCaseService` classes?

**No.** The same `@query` / `@mutation` decorated async classmethods work unchanged.
`FromContext` parameters continue to be filtered out of the public schema and
injected via `context_extractor` at execution time.

### Do I need to change my DTOs?

**No** — Pydantic models (including `DefineSubset`-generated DTOs) work as-is. They
get converted to GraphQL OBJECT types automatically.

### Does `Resolver().resolve(dtos)` still work inside service methods?

**Yes**, and you must call it yourself if you want Resolver behavior (e.g., auto-load
relationship fields on returned DTOs). The new GraphQL execution layer **does not**
wrap results in `Resolver` — service methods own that responsibility. This matches
the 2.x behavior.

### What if my DTO has `resolve_*` methods but I don't call `Resolver().resolve()`?

The `resolve_*` methods will not fire — returned DTOs will have `None` / default
values for those fields. This is intentional and matches the 2.x pattern where the
service method was responsible for invoking `Resolver`.

### Where did `ServiceIntrospector` go?

It was an internal class that generated SDL-style strings for the old MCP's
`describe_service` tool. It's replaced by `ComposeSchema`, which generates a real
GraphQL schema (introspection JSON + SDL). You shouldn't have been importing
`ServiceIntrospector` directly; if you were, use `build_compose_schema(app)` and
then `schema.render_sdl()` or `schema.render_method_sdl(service, method)`.
