Skip to content

[all-mcp] Apply int64ArgValueStrict to Server.swift direct callsites (sister to #22) #33

@kiki830621

Description

@kiki830621

Problem

int64ArgValue (non-strict, returns Int64?) still serves 20+ direct callsites in Server.swift for arg parsing. Each callsite has the same #22 bug shape:

guard let chatId = int64ArgValue(args, "chat_id") else {
    return errorResult("chat_id is required")
}

When user passes .string("not-a-number") for chat_id / message_id / user_id / from_chat_id / reply_to_message_id, int64ArgValue silently returns nil → caller throws misleading "X is required" (the key was present, the value was the wrong type).

Affected callsites in Server.swift (count via grep -c 'int64ArgValue(' = 20 in Server.swift after #22-cluster ship):

Line Tool Arg
L423 get_user user_id
L437 get_chat chat_id
L467 send_message chat_id
L471 send_message reply_to_message_id (optional — silent nil OK semantically)
L475-476 edit_message chat_id, message_id
L483 delete_messages chat_id
L491-492 forward_messages chat_id, from_chat_id
L499 search_messages chat_id
L508 get_chat_members chat_id
L515-516 pin_message chat_id, message_id
L523-524 unpin_message chat_id, message_id
L530 set_chat_title chat_id
L537 set_chat_description chat_id
L544 (continued) ...
L559-560 (continued) ...

Sister concern surfaced during #22/#23/#25 cluster implementation (PR #32). The cluster scoped the fix to the parser layer (HandlerArgs.swift × 3 callsites) because cascading try int64ArgValueStrict through 20+ callsites is a separate blast-radius decision.

Type

bug (consistency — sister to #22)

Expected

Two viable approaches; diagnose-time decision:

Option A — Swap to strict at all callsites, propagate try

Each callsite becomes:

let chatId: Int64
do {
    guard let id = try int64ArgValueStrict(args, "chat_id") else {
        return errorResult("chat_id is required")
    }
    chatId = id
} catch {
    return errorResultFromParse(error)
}

Pros: maximum precision per-callsite. Cons: 20+ × 8 lines of boilerplate.

Option B — Helper int64ArgValueRequired(args, key)throws Int64

internal func int64ArgValueRequired(_ args: [String: Value], _ key: String) throws -> Int64 {
    guard let value = try int64ArgValueStrict(args, key) else {
        throw HandlerArgError(message: "\(key) is required")
    }
    return value
}

Each callsite becomes:

let chatId = try int64ArgValueRequired(args, "chat_id")

The outer handleToolCall do { ... } catch block already catches HandlerArgError via errorResultFromParse (PR #32 drive-by). Mechanically minimal.

Pros: minimal change per callsite; matches #22 helper-layer philosophy. Cons: slightly different error path (caught at outer level, not at case arm — but errorResultFromParse handles both message types identically).

Option C — Wrap each case arm in do/catch + use parser-style helper extraction

Move each tool's arg parsing into a parseXxxArgs function (mirroring parseGetChatHistoryArgs). High consistency benefit but ~20 new struct types + parser functions. Heavy.

Acceptance

Code Reference

Related

Source: surfaced during /idd-implement #25 reproduction (Step 5.7)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions