SQLite-HIS-Mock-MCP工具实施计划.md 26 KB


doc_id: DEV-202606-004 feature_id: FEAT-202606-003-sqlite-his-mock-mcp-tools type: dev-progress title: SQLite HIS Mock MCP Tools Implementation Plan status: implemented owner: 医梦研发团队 created_at: 2026-06-04 updated_at: 2026-06-04 reviewers: [] related_docs:

  • PRD-202606-001
  • DEV-202606-003 related_modules:
  • emoon-ai-mcp
  • emoon-openplatform tags:
  • SQLite
  • HIS Mock
  • MCP ---

SQLite HIS Mock MCP Tools Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Build the demo HIS interface set inside the AI platform process, backed by SQLite, so registration demo workflows can call controlled MCP Tool APIs for patient, department, doctor, schedule, slot lock, mock payment, and appointment creation.

Architecture: Implement the mock HIS adapter in emoon-ai-mcp, because HIS Mock/Real Adapter belongs to MCP per the engineering constraints. Expose the v1.2 standard Tool API (GET /tools/catalog, POST /tools/{toolName}/invoke) and keep existing /api/mcp/his/tools/* endpoints as compatibility wrappers. Use an isolated SQLite DataSource and JdbcTemplate instead of the platform MySQL/dynamic datasource.

Tech Stack: Java 17, Spring Boot 3, Maven, JUnit 5, AssertJ, Mockito, SQLite JDBC, Spring JdbcTemplate.


Required Documents Before Coding

Read these completely before touching code:

  • docs/standards/AI中台工程约束.md
  • docs/standards/工程规约生效说明.md
  • docs/initiatives/FEAT-202606-001-unified-entry-client/对外接口联调基准.md
  • docs/api/openapi/医梦AI中台开放平台接口-v1.2.openapi.yaml
  • docs/initiatives/FEAT-202606-001-unified-entry-client/需求文档.md
  • docs/initiatives/FEAT-202606-001-unified-entry-client/dev-progress/统一入口挂号演示实施计划.md
  • docs/initiatives/FEAT-202606-001-unified-entry-client/dev-progress/演示闭环纵向任务拆分.md

Important constraints:

  • Ordinary terminal clients must not call Tool APIs directly.
  • Tool APIs are for controlled adapters/internal services.
  • HIS writes must be idempotent.
  • Registration/payment demo responses must always carry an explicit Mock marker.
  • Do not put this implementation in emoon-openplatform, emoon-ai-agent, or emoon-ai-card.
  • Do not connect SQLite to the AI platform MySQL/dynamic datasource.

Interface Scope

Implement these v1.2-compatible endpoints:

Method Path Purpose
GET /tools/catalog Return authorized demo tool catalog
POST /tools/{toolName}/invoke Invoke a controlled HIS/payment demo tool

Keep these compatibility endpoints by delegating to the new invoke service:

Method Path
POST /api/mcp/his/tools/get_departments
POST /api/mcp/his/tools/get_doctors
POST /api/mcp/his/tools/get_doctor_schedules
POST /api/mcp/his/tools/create_registration
POST /api/mcp/his/tools/invoke

Tool names to implement:

Tool Risk Type Demo Use
his.searchPatient L2_SENSITIVE_READ read Find patient before filing
his.createPatient L3_BUSINESS_WRITE write Create demo patient file
his.queryDepartments L1_QUERY read Department recommendation card
his.queryDoctors L1_QUERY read Doctor selection card
his.querySchedules L1_QUERY read Time slot selection card
his.lockSchedule L3_BUSINESS_WRITE write Lock selected slot for 5 minutes
his.releaseSchedule L3_BUSINESS_WRITE write Release slot lock
payment.createOrder L4_FINANCIAL_ACTION write Create Mock payment QR payload
payment.queryStatus L4_FINANCIAL_ACTION read Read Mock payment status
payment.markMockPaid L4_FINANCIAL_ACTION write Demo-only simulated payment success
his.createRegistration L3_BUSINESS_WRITE write Create appointment after payment
his.queryRegistration L2_SENSITIVE_READ read Read appointment success detail

File Structure

Maven

  • Modify: pom.xml
    • Add property sqlite-jdbc.version.
    • Add dependencyManagement entry for org.xerial:sqlite-jdbc.
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/pom.xml
    • Add dependency org.xerial:sqlite-jdbc.

SQLite Configuration

  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/config/HisMockProperties.java
    • Holds enabled, jdbcUrl, resetOnStart, lockTtlSeconds.
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/config/HisMockSqliteConfig.java
    • Creates isolated hisMockDataSource and hisMockJdbcTemplate.
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/config/HisMockDatabaseInitializer.java
    • Runs schema.sql and seed.sql.
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/resources/db/his-mock/schema.sql
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/resources/db/his-mock/seed.sql

Domain and Tool DTO

  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/client/HisClient.java
    • Extend from existing query/create methods to demo registration flow methods.
  • Create/modify domain files under:
    • emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/domain/
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/tool/dto/ToolInvokeRequest.java
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/tool/dto/ToolContext.java
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/tool/dto/ToolInvokeData.java
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/tool/dto/ToolCatalogItem.java

These DTOs must match the OpenAPI schema:

{
  "toolCallId": "tool-call-001",
  "context": {
    "projectId": "demo-project",
    "cardInstanceId": "card-001",
    "traceId": "trace-001"
  },
  "input": {},
  "riskLevel": "L1_QUERY"
}

Response data shape:

{
  "toolCallId": "tool-call-001",
  "toolName": "his.queryDepartments",
  "status": "success",
  "result": {
    "mock": true
  }
}

Application Services

  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/client/SqliteMockHisClient.java
    • SQLite implementation of HisClient.
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/application/HisToolCatalogService.java
    • Returns tool metadata and schemas.
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/application/HisToolInvokeService.java
    • Validates tool name/risk/idempotency and dispatches to HisClient.
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/application/HisMockIdempotencyService.java
    • Persists idempotency results for write tools.

Keep McpToolService as the internal facade used by existing agent/card code, but delegate to SqliteMockHisClient instead of in-memory data.

Controllers

  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/web/ToolController.java
    • GET /tools/catalog
    • POST /tools/{toolName}/invoke
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/main/java/com/emoon/mcp/his/web/HisMcpController.java
    • Delegate to HisToolInvokeService.
    • Keep old endpoint response wrapper McpToolResponse.

Tests

  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/test/java/com/emoon/mcp/his/client/SqliteMockHisClientTest.java
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/test/java/com/emoon/mcp/his/application/HisToolCatalogServiceTest.java
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/test/java/com/emoon/mcp/his/application/HisToolInvokeServiceTest.java
  • Create: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/test/java/com/emoon/mcp/his/web/ToolControllerTest.java
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/test/java/com/emoon/ai/mcp/application/McpToolServiceTest.java

SQLite Schema

Use these tables:

CREATE TABLE IF NOT EXISTS his_patient (
  patient_id TEXT PRIMARY KEY,
  patient_name TEXT NOT NULL,
  id_card TEXT,
  phone TEXT,
  gender TEXT,
  birth_date TEXT,
  created_at TEXT NOT NULL
);

CREATE UNIQUE INDEX IF NOT EXISTS uk_his_patient_id_card ON his_patient(id_card) WHERE id_card IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_his_patient_phone ON his_patient(phone);

CREATE TABLE IF NOT EXISTS his_department (
  department_id TEXT PRIMARY KEY,
  department_name TEXT NOT NULL,
  description TEXT,
  location TEXT,
  sort_no INTEGER NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS his_doctor (
  doctor_id TEXT PRIMARY KEY,
  department_id TEXT NOT NULL,
  doctor_name TEXT NOT NULL,
  title TEXT NOT NULL,
  specialty TEXT,
  consultation_fee INTEGER NOT NULL,
  FOREIGN KEY(department_id) REFERENCES his_department(department_id)
);

CREATE TABLE IF NOT EXISTS his_schedule (
  schedule_id TEXT PRIMARY KEY,
  doctor_id TEXT NOT NULL,
  schedule_date TEXT NOT NULL,
  time_period TEXT NOT NULL,
  start_time TEXT NOT NULL,
  end_time TEXT NOT NULL,
  total_count INTEGER NOT NULL,
  remaining_count INTEGER NOT NULL,
  fee INTEGER NOT NULL,
  FOREIGN KEY(doctor_id) REFERENCES his_doctor(doctor_id)
);

CREATE TABLE IF NOT EXISTS his_slot_lock (
  lock_id TEXT PRIMARY KEY,
  schedule_id TEXT NOT NULL,
  patient_id TEXT NOT NULL,
  status TEXT NOT NULL,
  expires_at TEXT NOT NULL,
  idempotency_key TEXT NOT NULL UNIQUE,
  created_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS his_payment_order (
  order_id TEXT PRIMARY KEY,
  lock_id TEXT NOT NULL,
  amount INTEGER NOT NULL,
  status TEXT NOT NULL,
  qr_content TEXT NOT NULL,
  idempotency_key TEXT NOT NULL UNIQUE,
  created_at TEXT NOT NULL,
  paid_at TEXT
);

CREATE TABLE IF NOT EXISTS his_appointment (
  appointment_id TEXT PRIMARY KEY,
  appointment_no TEXT NOT NULL UNIQUE,
  patient_id TEXT NOT NULL,
  department_id TEXT NOT NULL,
  doctor_id TEXT NOT NULL,
  schedule_id TEXT NOT NULL,
  lock_id TEXT NOT NULL,
  order_id TEXT NOT NULL,
  status TEXT NOT NULL,
  queue_no TEXT NOT NULL,
  idempotency_key TEXT NOT NULL UNIQUE,
  created_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS his_idempotency_record (
  idempotency_key TEXT PRIMARY KEY,
  tool_name TEXT NOT NULL,
  request_hash TEXT NOT NULL,
  response_json TEXT NOT NULL,
  created_at TEXT NOT NULL
);

Seed at least:

  • 3 departments: dept_neurology, dept_digestive, dept_general
  • 4 doctors across these departments
  • Schedule rows for today + 1 day and today + 2 days
  • A demo patient patient_demo_001

Task 1: Add SQLite Dependency and Configuration

Files:

  • Modify: pom.xml
  • Modify: emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/pom.xml
  • Create: .../config/HisMockProperties.java
  • Create: .../config/HisMockSqliteConfig.java

  • [ ] Step 1: Add failing configuration test

Create emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp/src/test/java/com/emoon/mcp/his/config/HisMockSqliteConfigTest.java:

package com.emoon.mcp.his.config;

import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;

import static org.assertj.core.api.Assertions.assertThat;

class HisMockSqliteConfigTest {

    @Test
    void createsJdbcTemplateForSqliteDatasource() {
        HisMockProperties properties = new HisMockProperties();
        properties.setJdbcUrl("jdbc:sqlite::memory:");
        properties.setEnabled(true);
        properties.setLockTtlSeconds(300);

        HisMockSqliteConfig config = new HisMockSqliteConfig();
        JdbcTemplate jdbcTemplate = config.hisMockJdbcTemplate(config.hisMockDataSource(properties));

        Integer result = jdbcTemplate.queryForObject("select 1", Integer.class);
        assertThat(result).isEqualTo(1);
    }
}
  • Step 2: Run failing test

Run:

mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp -DskipTests=false -Dtest=HisMockSqliteConfigTest test

Expected: FAIL because config classes and SQLite dependency do not exist.

  • Step 3: Add minimal implementation

Add sqlite-jdbc.version and dependencyManagement in root pom.xml, then dependency in emoon-ai-mcp/pom.xml.

Create HisMockProperties:

package com.emoon.mcp.his.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "emoon.his.mock")
public class HisMockProperties {
    private boolean enabled = true;
    private String jdbcUrl = "jdbc:sqlite:file:./data/his-mock.db";
    private boolean resetOnStart = false;
    private long lockTtlSeconds = 300;
}

Create HisMockSqliteConfig:

package com.emoon.mcp.his.config;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;

@Configuration
@EnableConfigurationProperties(HisMockProperties.class)
public class HisMockSqliteConfig {

    @Bean
    public DataSource hisMockDataSource(HisMockProperties properties) {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.sqlite.JDBC");
        dataSource.setUrl(properties.getJdbcUrl());
        return dataSource;
    }

    @Bean
    public JdbcTemplate hisMockJdbcTemplate(DataSource hisMockDataSource) {
        return new JdbcTemplate(hisMockDataSource);
    }
}
  • Step 4: Run test

Run the same Maven command. Expected: PASS.

Task 2: Initialize SQLite Schema and Seed Data

Files:

  • Create: .../config/HisMockDatabaseInitializer.java
  • Create: .../resources/db/his-mock/schema.sql
  • Create: .../resources/db/his-mock/seed.sql
  • Test: .../config/HisMockDatabaseInitializerTest.java

  • [ ] Step 1: Write failing initializer test

Test must create an in-memory SQLite datasource, run initializer, then assert table and seed row exist:

Integer departments = jdbcTemplate.queryForObject("select count(*) from his_department", Integer.class);
assertThat(departments).isGreaterThanOrEqualTo(3);
  • [ ] Step 2: Run failing test

    mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp -DskipTests=false -Dtest=HisMockDatabaseInitializerTest test
    

Expected: FAIL because initializer and SQL resources do not exist.

  • Step 3: Implement initializer

Use ResourceDatabasePopulator with schema.sql and seed.sql. If resetOnStart=true, run a small drop script before schema creation. Keep default resetOnStart=false.

  • Step 4: Run test

Expected: PASS.

Task 3: Implement Query Tools

Files:

  • Modify: HisClient.java
  • Create: SqliteMockHisClient.java
  • Modify/create domain classes under .../domain
  • Test: SqliteMockHisClientTest.java

  • [ ] Step 1: Write failing tests

Cover:

  • queryDepartments returns seeded departments with mock=true available in wrapper result.
  • queryDoctors("dept_neurology") returns neurologists.
  • querySchedules(doctorId, date) returns remaining count and fee.
  • unknown department/doctor returns empty list, not exception.

  • [ ] Step 2: Run failing tests

    mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp -DskipTests=false -Dtest=SqliteMockHisClientTest test
    
  • [ ] Step 3: Implement SQLite reads

Use JdbcTemplate.query(...); do not add MyBatis Mapper for this SQLite mock.

  • Step 4: Run tests

Expected: PASS.

Task 4: Implement Patient Filing Tools

Files:

  • Modify: HisClient.java
  • Modify: SqliteMockHisClient.java
  • Add domain: HisPatient.java
  • Test: SqliteMockHisClientTest.java

  • [ ] Step 1: Write failing tests

Cover:

  • searchPatient finds by phone.
  • searchPatient finds by id card.
  • createPatient creates a patient and returns same patient for duplicate idempotency key.
  • duplicate id card returns existing patient instead of creating a second row.

  • [ ] Step 2: Run failing tests

Expected: FAIL.

  • Step 3: Implement patient methods

Use his_patient and his_idempotency_record. Return:

{
  "patientId": "patient_xxx",
  "patientName": "张三",
  "mock": true
}
  • Step 4: Run tests

Expected: PASS.

Task 5: Implement Slot Lock and Release

Files:

  • Modify: HisClient.java
  • Modify: SqliteMockHisClient.java
  • Add domain: HisSlotLock.java
  • Test: SqliteMockHisClientTest.java

  • [ ] Step 1: Write failing tests

Cover:

  • lock succeeds when remaining_count > 0.
  • lock returns same result for duplicate idempotency key.
  • lock fails when schedule has no remaining slots.
  • release changes lock status to RELEASED.
  • expired locks do not block new lock attempts.

  • [ ] Step 2: Run failing tests

Expected: FAIL.

  • Step 3: Implement lock/release

Use a transaction. For demo SQLite, synchronize around write methods in SqliteMockHisClient to avoid double-lock in same JVM.

Do not decrement remaining_count on lock. Decrement only when appointment is created.

  • Step 4: Run tests

Expected: PASS.

Task 6: Implement Mock Payment Tools

Files:

  • Modify: HisClient.java
  • Modify: SqliteMockHisClient.java
  • Add domain: HisPaymentOrder.java
  • Test: SqliteMockHisClientTest.java

  • [ ] Step 1: Write failing tests

Cover:

  • create payment order requires active lock.
  • order amount comes from schedule fee.
  • QR content starts with demo-payment://orderId=.
  • duplicate create order idempotency key returns same order.
  • markMockPaid changes status to PAID.
  • payment result includes mock=true and mockPayment=true.

  • [ ] Step 2: Run failing tests

Expected: FAIL.

  • Step 3: Implement payment methods

Statuses:

PENDING
PAID
CANCELLED

Do not use WeChat, Alipay,医保, or any real payment brand in response fields.

  • Step 4: Run tests

Expected: PASS.

Task 7: Implement Appointment Creation

Files:

  • Modify: HisClient.java
  • Modify: SqliteMockHisClient.java
  • Modify: HisRegistrationRequest.java
  • Modify: HisRegistrationResult.java
  • Test: SqliteMockHisClientTest.java

  • [ ] Step 1: Write failing tests

Cover:

  • appointment creation requires paid order.
  • appointment decrements remaining_count.
  • duplicate idempotency key returns same appointment.
  • unpaid order fails and does not decrement stock.
  • response includes appointmentId, appointmentNo, queueNo, mock=true.

  • [ ] Step 2: Run failing tests

Expected: FAIL.

  • Step 3: Implement appointment method

Use his_appointment. Generate:

appointmentId = appt_<12 chars>
appointmentNo = REGyyyyMMdd<sequence>
queueNo = A01/A02...
  • Step 4: Run tests

Expected: PASS.

Task 8: Implement Tool Catalog

Files:

  • Create: HisToolCatalogService.java
  • Create: ToolCatalogItem.java
  • Test: HisToolCatalogServiceTest.java

  • [ ] Step 1: Write failing catalog test

Assert catalog contains exactly the scoped demo tools from this plan and each item has:

  • toolName
  • toolType
  • description
  • inputSchema
  • outputSchema
  • riskLevel

  • [ ] Step 2: Run failing test

Expected: FAIL.

  • Step 3: Implement catalog

Keep schemas small but concrete. Example:

{
  "toolName": "his.queryDoctors",
  "toolType": "read",
  "riskLevel": "L1_QUERY",
  "inputSchema": {
    "type": "object",
    "required": ["departmentId"],
    "properties": {
      "departmentId": {"type": "string"}
    }
  }
}
  • Step 4: Run test

Expected: PASS.

Task 9: Implement Tool Invoke Service

Files:

  • Create: HisToolInvokeService.java
  • Create: ToolInvokeRequest.java
  • Create: ToolContext.java
  • Create: ToolInvokeData.java
  • Test: HisToolInvokeServiceTest.java

  • [ ] Step 1: Write failing invoke tests

Cover:

  • unknown tool returns status=failed.
  • toolCallId is echoed.
  • successful read tool returns status=success.
  • write tool without idempotency key returns status=failed.
  • risk mismatch returns status=forbidden.
  • payment.markMockPaid is allowed only because result has explicit mockPayment=true.

  • [ ] Step 2: Run failing test

Expected: FAIL.

  • Step 3: Implement dispatcher

Dispatch by path toolName, not request body toolName, to match OpenAPI:

public ToolInvokeData invoke(String toolName, String idempotencyKey, ToolInvokeRequest request)

Read idempotency key from:

  1. X-Emoon-Idempotency-Key header
  2. request.input.idempotencyKey

Write tools must have one of them.

  • Step 4: Run test

Expected: PASS.

Task 10: Add Standard Tool API Controller

Files:

  • Create: ToolController.java
  • Test: ToolControllerTest.java

  • [ ] Step 1: Write failing controller tests

Use @WebMvcTest(ToolController.class) or direct controller test depending on project test stability.

Assert:

  • GET /tools/catalog returns R.ok style response with data.tools.
  • POST /tools/his.queryDepartments/invoke accepts OpenAPI ToolInvokeRequest.
  • header X-Emoon-Idempotency-Key is passed to invoke service.

  • [ ] Step 2: Run failing test

Expected: FAIL.

  • Step 3: Implement controller

Controller methods:

@GetMapping("/tools/catalog")
public R<Map<String, Object>> catalog()

@PostMapping("/tools/{toolName}/invoke")
public R<ToolInvokeData> invoke(
    @PathVariable String toolName,
    @RequestHeader(value = "X-Emoon-Idempotency-Key", required = false) String idempotencyKey,
    @Valid @RequestBody ToolInvokeRequest request)
  • Step 4: Run test

Expected: PASS.

Task 11: Update Existing HIS MCP Compatibility Controller

Files:

  • Modify: HisMcpController.java
  • Modify: old tool DTOs if needed
  • Test: ToolControllerTest.java or HisMcpControllerTest.java

  • [ ] Step 1: Write failing compatibility tests

Cover old calls:

  • /api/mcp/his/tools/get_departments
  • /api/mcp/his/tools/get_doctors
  • /api/mcp/his/tools/get_doctor_schedules
  • /api/mcp/his/tools/create_registration
  • /api/mcp/his/tools/invoke with old toolName values

  • [ ] Step 2: Run failing tests

Expected: FAIL after service replacement until compatibility mapping is done.

  • Step 3: Implement compatibility mapping

Map old names:

Old New
his_get_departments his.queryDepartments
his_get_doctors his.queryDoctors
his_get_doctor_schedules his.querySchedules
his_create_registration his.createRegistration

For old create_registration, if there is no paid order in request, return a clear failed demo response rather than silently bypassing payment. If compatibility with existing tests requires success, add an explicit demo-only path with generated lock/order paid in one transaction and response mock=true.

  • Step 4: Run tests

Expected: PASS.

Task 12: Update Internal McpToolService

Files:

  • Modify: McpToolService.java
  • Modify: McpToolServiceTest.java

  • [ ] Step 1: Update failing tests

Replace mock expectations so:

  • lockSchedule calls real HisClient.lockSchedule(...) style method.
  • createAppointment includes orderId or delegates to the new registration flow.
  • returned map still contains mock=true.

  • [ ] Step 2: Run failing test

Expected: FAIL.

  • Step 3: Implement facade update

Keep existing public methods if used by agent/card code, but route through new client methods. Do not duplicate business rules here.

  • Step 4: Run tests

Expected: PASS.

Task 13: Add Demo Curl Scripts

Files:

  • Create: scripts/demo-registration/tools-catalog.sh
  • Create: scripts/demo-registration/registration-tool-flow.sh

  • [ ] Step 1: Write scripts

The flow script must call:

GET /tools/catalog
POST /tools/his.searchPatient/invoke
POST /tools/his.queryDepartments/invoke
POST /tools/his.queryDoctors/invoke
POST /tools/his.querySchedules/invoke
POST /tools/his.lockSchedule/invoke
POST /tools/payment.createOrder/invoke
POST /tools/payment.markMockPaid/invoke
POST /tools/his.createRegistration/invoke
  • Step 2: Include expected response markers

Each write step should echo the idempotency key and expected response marker:

mock=true
mockPayment=true for payment tools
status=success

Task 14: Verification

  • [ ] Step 1: Run MCP module compile

    mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp -am -DskipTests compile
    

Expected: BUILD SUCCESS.

  • [ ] Step 2: Run MCP module tests

    mvn -pl emoon-infra/emoon-modules/emoon-ai/emoon-ai-mcp -DskipTests=false test
    

Expected: BUILD SUCCESS.

  • [ ] Step 3: Run architecture test

    mvn -pl emoon-admin -DskipTests=false -Dprofiles.active= -Dtest=AiPlatformArchitectureTest test
    

Expected: BUILD SUCCESS.

  • Step 4: Manual smoke test after startup

Start AI platform normally, then call:

curl -s http://localhost:8080/tools/catalog

Expected: response contains his.queryDepartments, his.createRegistration, and payment.createOrder.

Acceptance Criteria

  • AI platform starts with SQLite Mock HIS enabled by default in demo/dev.
  • /tools/catalog returns the demo HIS/payment catalog using v1.2 ToolCatalogItem shape.
  • /tools/{toolName}/invoke accepts v1.2 ToolInvokeRequest shape.
  • Query tools work without idempotency key.
  • Write tools require idempotency key.
  • Duplicate write calls return the original result.
  • Slot lock expires after configured TTL.
  • Payment is clearly Mock and does not reference real payment brands.
  • Appointment creation requires paid mock order.
  • Appointment creation decrements remaining schedule count exactly once.
  • Old /api/mcp/his/tools/* endpoints still work as compatibility wrappers.
  • No Controller or Mapper for this mock is added to emoon-openplatform.
  • No SQLite datasource is added to the platform MySQL dynamic datasource.