1
00:00:00,000 --> 00:00:04,638
Welcome to the show -- James,
picture this: your Bash tool finishes,

2
00:00:04,638 --> 00:00:10,561
and 18, 342 milliseconds later a message
pops into Slack saying,

3
00:00:10,561 --> 00:00:18,562
"Bash tool completed in 18342ms,"
straight from .claude/settings.

4
00:00:10,561 --> 00:00:18,562
json.

5
00:00:18,562 --> 00:00:21,043
No wrapper script.

6
00:00:21,043 --> 00:00:22,084
No glue code.

7
00:00:22,084 --> 00:00:26,459
Just the hook talking DIRECTLY to an MCP
tool.Wait

8
00:00:26,459 --> 00:00:31,242
-- 18,
342 milliseconds is weirdly specific,

9
00:00:31,242 --> 00:00:32,558
which is why I love it.

10
00:00:32,558 --> 00:00:36,241
You're saying in Claude Code 2.

11
00:00:32,558 --> 00:00:36,241
1.118,

12
00:00:36,241 --> 00:00:38,401
a hook can use type: "mcp_tool" itself?

13
00:00:38,401 --> 00:00:51,662
Like the hook is the MCP client now,

14
00:00:51,662 --> 00:00:54,319
not some bash middleman pretending to be
one?Yeah,

15
00:00:54,319 --> 00:00:54,959
exactly.

16
00:00:54,959 --> 00:00:57,039
And that's the sneaky-big shift.

17
00:00:57,039 --> 00:01:03,515
In 2.

18
00:00:57,039 --> 00:01:03,515
1.118, hooks got this direct path: you
can set type to "mcp_tool",

19
00:01:03,515 --> 00:01:07,581
and then a PostToolUse hook can call
something like send_message on a

20
00:01:07,581 --> 00:01:08,878
Slack MCP server.

21
00:01:08,878 --> 00:01:13,570
Before that, the setup was a bit how ya
goin' -- the hook fired a shell

22
00:01:13,570 --> 00:01:16,792
script, the shell script then invoked the
MCP tool,

23
00:01:16,792 --> 00:01:18,868
and suddenly you've got extra config,

24
00:01:18,868 --> 00:01:23,476
separate auth handling, and this leaky
abstraction where the "automation"

25
00:01:23,476 --> 00:01:27,253
is really just duct tape in a trench
coat.The "separate auth

26
00:01:27,253 --> 00:01:29,116
handling" part is the one that jumps out.

27
00:01:29,116 --> 00:01:32,475
Because if the bash script has to know
how to reach Slack,

28
00:01:32,475 --> 00:01:35,984
or GitHub, or whatever,
that means credentials,

29
00:01:35,984 --> 00:01:38,872
environment differences, quoting bugs...

30
00:01:38,872 --> 00:01:40,156
all the gross stuff.

31
00:01:40,156 --> 00:01:44,850
It stops feeling like one system and
starts feeling like three systems

32
00:01:44,850 --> 00:01:47,400
stacked on top of each other.

33
00:01:44,850 --> 00:01:47,400
Right.

34
00:01:47,400 --> 00:01:51,122
And if you've ever babysat one of those
scripts at midnight -- I have,

35
00:01:51,122 --> 00:01:55,077
different context, same pain -- it's
always the silly bit that breaks.

36
00:01:55,077 --> 00:01:56,757
A path changes.

37
00:01:56,757 --> 00:01:58,998
A token isn't available in one shell.

38
00:01:58,998 --> 00:02:03,618
A JSON payload gets mangled because you
forgot how many quotes bash wants

39
00:02:03,618 --> 00:02:04,043
today.

40
00:02:04,043 --> 00:02:06,524
With the direct MCP tool
call,

41
00:02:06,524 --> 00:02:09,644
the hook config lives where the behavior
lives.

42
00:02:09,644 --> 00:02:14,824
That's the cleaner mental model.
Walk me through the concrete

43
00:02:14,824 --> 00:02:15,401
flow, though.

44
00:02:15,401 --> 00:02:17,079
I want the exact chain.

45
00:02:17,079 --> 00:02:20,598
Tool runs, hook sees it,
then what?Okay.

46
00:02:20,598 --> 00:02:22,637
Say a Bash tool finishes.

47
00:02:22,637 --> 00:02:25,522
That emits a PostToolUse event.

48
00:02:25,522 --> 00:02:30,720
In .claude/settings.

49
00:02:25,522 --> 00:02:30,720
json, you've got a hook configured for
that event,

50
00:02:30,720 --> 00:02:32,880
and instead of launching a script,

51
00:02:32,880 --> 00:02:36,400
the hook uses type: "mcp_tool".

52
00:02:36,400 --> 00:02:40,527
The payload templates in {{duration_ms}}
-- we'll get to why that field

53
00:02:40,527 --> 00:02:42,962
matters in a sec -- and then it calls,

54
00:02:42,962 --> 00:02:46,562
say,
send_message on your Slack MCP server.

55
00:02:46,562 --> 00:02:54,220
The message body is literally "Bash tool
completed in 18342ms," and the

56
00:02:54,220 --> 00:03:00,605
target channel is
#dev-activity.So

57
00:02:54,220 --> 00:03:00,605
#dev-activity gets

58
00:03:00,605 --> 00:03:04,234
that post as a side effect of the tool
event itself.

59
00:03:04,234 --> 00:03:06,223
Not the agent deciding in natural
language,

60
00:03:06,223 --> 00:03:10,647
"hey maybe I should tell Slack." Not a
shell script doing an impersonation

61
00:03:10,647 --> 00:03:11,743
of integration logic.

62
00:03:11,743 --> 00:03:15,640
The hook is just wired to the event.

63
00:03:11,743 --> 00:03:15,640
Spot on.

64
00:03:15,640 --> 00:03:18,445
And that distinction matters more than it
sounds.

65
00:03:18,445 --> 00:03:21,961
Because "Claude decided to say something"
is fuzzy.

66
00:03:21,961 --> 00:03:26,921
"This event triggers this MCP tool call"
is deterministic.

67
00:03:26,921 --> 00:03:28,684
That's automation.

68
00:03:28,684 --> 00:03:33,065
It's less like asking a coworker to
remember to text you and more like

69
00:03:33,065 --> 00:03:36,087
wiring a relay in the shed -- ugly
analogy,

70
00:03:36,087 --> 00:03:38,806
but once the circuit closes,
the light comes on.

71
00:03:38,806 --> 00:03:41,917
Every time.No, that's a good one.

72
00:03:41,917 --> 00:03:47,233
And I think the surprise here is people
will hear "convenience feature"

73
00:03:47,233 --> 00:03:49,755
and think, cool, fewer lines of config.

74
00:03:49,755 --> 00:03:52,640
But the real change is architectural.

75
00:03:52,640 --> 00:03:55,923
Hooks used to be scripts that react to
events.

76
00:03:55,923 --> 00:04:00,082
Now they're first-class automation nodes
that can talk to Slack,

77
00:04:00,082 --> 00:04:05,401
Linear, GitHub -- any connected MCP
server.Yep.

78
00:04:05,401 --> 00:04:07,080
That's the bit I'd underline in red.

79
00:04:07,080 --> 00:04:10,198
If a hook can directly call an MCP tool,

80
00:04:10,198 --> 00:04:12,672
then the hook is no longer just a local
afterthought.

81
00:04:12,672 --> 00:04:16,348
It's participating in the same tool
ecosystem as the agent.

82
00:04:16,348 --> 00:04:19,550
That means a completed edit could log to
GitHub,

83
00:04:19,550 --> 00:04:22,753
a failed command could open or update a
Linear issue,

84
00:04:22,753 --> 00:04:27,059
and a long-running task could ping Slack
without this weird shell-wrapper

85
00:04:27,059 --> 00:04:32,201
hop in between.

86
00:04:27,059 --> 00:04:32,201
And because it's all in .claude/settings.

87
00:04:27,059 --> 00:04:32,201
json,

88
00:04:32,201 --> 00:04:34,320
the behavior is visible.

89
00:04:34,320 --> 00:04:36,197
That's another underrated thing.

90
00:04:36,197 --> 00:04:40,441
When the logic lives in random scripts
sprinkled around a repo,

91
00:04:40,441 --> 00:04:42,121
nobody knows what's actually happening.

92
00:04:42,121 --> 00:04:44,681
When it lives in one config file,

93
00:04:44,681 --> 00:04:46,639
at least you can audit it.
Totally.

94
00:04:46,639 --> 00:04:49,920
The old pattern made hooks feel bolted on.

95
00:04:49,920 --> 00:04:52,958
Useful, sure, but bolted on.

96
00:04:52,958 --> 00:04:56,155
This makes them feel native.

97
00:04:56,155 --> 00:04:58,400
And once something feels native,

98
00:04:58,400 --> 00:05:02,474
people stop using it just for
notifications and start using it for

99
00:04:58,400 --> 00:05:02,474
process.

100
00:05:02,474 --> 00:05:06,000
That's where it gets interesting...

101
00:05:06,000 --> 00:05:08,481
and a tiny bit spicy.Yeah,

102
00:05:08,481 --> 00:05:12,239
because 2.

103
00:05:08,481 --> 00:05:12,239
1.119 adds the other half of this.

104
00:05:12,239 --> 00:05:17,915
PostToolUse and PostToolUseFailure hook
inputs now include duration_ms.

105
00:05:17,915 --> 00:05:23,172
And the definition is SUPER clean: it
measures only the tool's execution

106
00:05:23,172 --> 00:05:23,588
time.

107
00:05:23,588 --> 00:05:27,031
Not permission prompts,
not PreToolUse processing,

108
00:05:27,031 --> 00:05:29,110
not Claude's internal decision time.

109
00:05:29,110 --> 00:05:32,440
Just the tool run itself.Let me try to say that back.

110
00:05:32,440 --> 00:05:36,278
If Bash takes 31 seconds to actually
execute,

111
00:05:36,278 --> 00:05:40,041
but there was some faffing about before
that -- permissions,

112
00:05:40,041 --> 00:05:45,394
thinking, pre-checks -- duration_ms
ignores the faffing and clocks the

113
00:05:45,394 --> 00:05:47,717
31 seconds?

114
00:05:45,394 --> 00:05:47,717
Exactly.

115
00:05:47,717 --> 00:05:50,516
The "31 seconds" token is the one that
matters.

116
00:05:50,516 --> 00:05:53,158
It's basically a pure I/O signal.

117
00:05:53,158 --> 00:05:58,257
So if you want a rule like "alert only if
Bash takes longer than 30 seconds,"

118
00:05:58,257 --> 00:06:02,473
duration_ms is useful because it's not
polluted by everything else happening

119
00:06:02,473 --> 00:06:03,309
around the tool.

120
00:06:03,309 --> 00:06:09,421
Same for "skip logging sub-200ms edits."
Two hundred milliseconds means

121
00:06:09,421 --> 00:06:14,320
the edit itself was fast, not that the
whole agent experience felt fast.

122
00:06:09,421 --> 00:06:14,320
That

123
00:06:14,320 --> 00:06:17,561
"sub-200ms edits" example is gold,

124
00:06:17,561 --> 00:06:17,999
actually.

125
00:06:17,999 --> 00:06:21,444
Because if you log EVERY Write or Edit
call,

126
00:06:21,444 --> 00:06:23,685
you get a firehose.

127
00:06:23,685 --> 00:06:26,243
Proper garden-hose-to-the-face stuff.

128
00:06:26,243 --> 00:06:31,981
But if you say, only log Write or Edit
when duration_ms crosses some

129
00:06:31,981 --> 00:06:37,105
threshold, now the noise drops and the
signal pops.

130
00:06:37,105 --> 00:06:39,240
Right -- and that's where matchers matter.

131
00:06:39,240 --> 00:06:43,809
The hook config can combine event
matching with performance thresholds.

132
00:06:43,809 --> 00:06:46,293
So you can target only Write calls,

133
00:06:46,293 --> 00:06:51,173
or only Edit calls, or only failures
through PostToolUseFailure,

134
00:06:51,173 --> 00:06:56,189
and then route only those events to an
MCP tool for logging or notification.

135
00:06:51,173 --> 00:06:56,189
And

136
00:06:56,189 --> 00:06:58,873
because failures have duration_ms too,

137
00:06:58,873 --> 00:07:01,114
you can start asking more useful
questions.

138
00:07:01,114 --> 00:07:02,792
Did the failure happen instantly,

139
00:07:02,792 --> 00:07:04,470
like a bad invocation?

140
00:07:04,470 --> 00:07:06,710
Or did it fail after 45 seconds,

141
00:07:06,710 --> 00:07:10,225
which smells more like a timeout or
external dependency issue?

142
00:07:10,225 --> 00:07:15,000
Same failure event, very different
story.Though this is where

143
00:07:15,000 --> 00:07:18,159
I wanna push on the "workflow control
layer" idea.

144
00:07:18,159 --> 00:07:20,877
Notifications are safe-ish.

145
00:07:20,877 --> 00:07:24,801
But once you have direct MCP calls plus
timing,

146
00:07:24,801 --> 00:07:29,115
it's tempting to build reactive systems:
if Bash exceeds 30 seconds,

147
00:07:29,115 --> 00:07:32,072
send a Slack message; if Edit fails twice,

148
00:07:32,072 --> 00:07:35,114
create a ticket; if Write takes too long,

149
00:07:35,114 --> 00:07:36,958
maybe trigger some follow-up action.

150
00:07:36,958 --> 00:07:42,150
And now your hook system isn't just
watching work -- it's steering

151
00:07:36,958 --> 00:07:42,150
work.

152
00:07:42,150 --> 00:07:45,043
I think that's true,
and I think it's the whole point.

153
00:07:45,043 --> 00:07:47,679
But yeah, there's a trap there.

154
00:07:47,679 --> 00:07:53,201
If the hook's MCP call is just a side
effect -- post to Slack,

155
00:07:53,201 --> 00:07:57,517
log somewhere,
maybe tag a Linear issue -- sweet as.

156
00:07:57,517 --> 00:08:02,072
If the hook kicks off something that
causes MORE agent activity,

157
00:08:02,072 --> 00:08:03,751
you can end up with a feedback loop.

158
00:08:03,751 --> 00:08:10,226
Tool finishes, hook fires,
MCP action triggers new activity,

159
00:08:10,226 --> 00:08:13,424
new activity triggers another hook...

160
00:08:13,424 --> 00:08:18,602
and suddenly even a kangaroo could trip
over that fresh code.

161
00:08:18,602 --> 00:08:20,476
The feedback loop is the scary part.

162
00:08:20,476 --> 00:08:24,074
Especially because it won't look scary at
first.

163
00:08:24,074 --> 00:08:25,515
It'll look elegant.

164
00:08:25,515 --> 00:08:31,073
"Oh cool, on failure we auto-create
something." Then that something pings

165
00:08:31,073 --> 00:08:33,993
another system, that system updates state,

166
00:08:33,993 --> 00:08:38,041
the agent sees state change, and now
you've built the software equivalent

167
00:08:38,041 --> 00:08:43,474
of a microphone pointed at a speaker.

168
00:08:38,041 --> 00:08:43,474
Exactly -- the awful squeal.

169
00:08:43,474 --> 00:08:47,395
And the clean timing signal makes the
automation more tempting,

170
00:08:47,395 --> 00:08:50,594
not less,
because now the rules can be smart.

171
00:08:50,594 --> 00:08:52,272
Longer than 30 seconds?

172
00:08:52,272 --> 00:08:53,234
Alert.

173
00:08:53,234 --> 00:08:54,911
Shorter than 200ms?

174
00:08:54,911 --> 00:08:55,789
Ignore.

175
00:08:55,789 --> 00:08:58,187
Failure plus long duration?

176
00:08:58,187 --> 00:08:58,986
Escalate.

177
00:08:58,986 --> 00:09:01,588
That's proper operational logic.

178
00:09:01,588 --> 00:09:05,869
So the upgrade is small on paper -- a new
type in 2.

179
00:09:01,588 --> 00:09:05,869
1.118,

180
00:09:05,869 --> 00:09:11,856
a new field in 2.

181
00:09:05,869 --> 00:09:11,856
1.119 -- but together they move hooks
from passive event

182
00:09:11,856 --> 00:09:16,478
listeners into something closer to a
policy layer.Yeah.

183
00:09:16,478 --> 00:09:18,558
It's the combination that matters.

184
00:09:18,558 --> 00:09:23,499
Direct MCP calls without duration_ms are
useful but kind of blunt.

185
00:09:23,499 --> 00:09:27,566
duration_ms without direct MCP calls is
informative but still clunky.

186
00:09:27,566 --> 00:09:31,403
Put them together,
and a hook can say: for this exact tool,

187
00:09:31,403 --> 00:09:35,080
under this exact condition,
call this exact external system.

188
00:09:35,080 --> 00:09:40,681
That's not "just notify me." That's
workflow design.

189
00:09:35,080 --> 00:09:40,681
And maybe the best

190
00:09:40,681 --> 00:09:43,361
version of that is boring on purpose.

191
00:09:43,361 --> 00:09:45,758
Use the timing to cut noise.

192
00:09:45,758 --> 00:09:50,473
Use the MCP call for side effects that
humans actually need.

193
00:09:50,473 --> 00:09:55,353
Don't let the hook become a tiny chaos
goblin running your whole stack.

194
00:09:55,353 --> 00:10:03,245
If your Slack channel learns
every 18342ms Bash run but your

195
00:10:03,245 --> 00:10:05,269
agent starts chasing its own tail,

196
00:10:05,269 --> 00:10:10,092
you've solved the wrong problem.
That's the question I'd leave

197
00:10:10,092 --> 00:10:15,217
people with: when your hooks can see how
long work took and can talk directly

198
00:10:15,217 --> 00:10:19,008
to the rest of your tools,
are you building observability...

199
00:10:19,008 --> 00:10:23,705
or are you quietly building a second
automation system inside the first

200
00:10:23,705 --> 00:10:26,680
one?Bit of both, probably.

201
00:10:26,680 --> 00:10:28,682
Catch you next time.
