Skip to content

Commit b3b0735

Browse files
committed
remove unnecessary byId
1 parent 915e3d2 commit b3b0735

File tree

13 files changed

+96
-27
lines changed

13 files changed

+96
-27
lines changed

epicshop/epic-me/app/routes/db-api.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ const methodSchemas = {
6464
id: z.number(),
6565
}),
6666

67+
getUser: z.object({}),
68+
6769
// Entry tag methods
6870
addTagToEntry: z.object({
6971
entryId: z.number(),
@@ -172,6 +174,12 @@ export async function action({ request, context }: Route.ActionArgs) {
172174
break
173175
}
174176

177+
case 'getUser': {
178+
methodSchemas.getUser.parse(params) // Validate but no params needed
179+
result = await context.db.getUserById(Number(userId))
180+
break
181+
}
182+
175183
case 'addTagToEntry': {
176184
const entryTagParams = methodSchemas.addTagToEntry.parse(params)
177185
result = await context.db.addTagToEntry(Number(userId), entryTagParams)

epicshop/epic-me/workers/db/client/dist/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ export declare class DBClient {
2727
deleteTag(id: number): Promise<boolean>;
2828
addTagToEntry(entryTag: NewEntryTag): Promise<EntryTag>;
2929
getEntryTags(entryId: number): Promise<Tag[]>;
30-
getUserById(id: number): Promise<User | null>;
30+
getUser(): Promise<User | null>;
3131
}
3232
export type { Entry, NewEntry, Tag, NewTag, EntryTag, NewEntryTag, EntryWithTags, User, };

epicshop/epic-me/workers/db/client/dist/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,10 @@ var DBClient = /** @class */ (function () {
184184
});
185185
};
186186
// User Methods
187-
DBClient.prototype.getUserById = function (id) {
187+
DBClient.prototype.getUser = function () {
188188
return __awaiter(this, void 0, void 0, function () {
189189
return __generator(this, function (_a) {
190-
return [2 /*return*/, this.makeRequest('getUserById', { id: id })];
190+
return [2 /*return*/, this.makeRequest('getUser')];
191191
});
192192
});
193193
};

epicshop/epic-me/workers/db/client/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ export class DBClient {
121121
}
122122

123123
// User Methods
124-
async getUserById(id: number): Promise<User | null> {
125-
return this.makeRequest<User | null>('getUserById', { id })
124+
async getUser(): Promise<User | null> {
125+
return this.makeRequest<User | null>('getUser')
126126
}
127127
}
128128

exercises/04.user/02.problem.user/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ You can also help users add tags to their entries and get all tags for an entry.
5959
}
6060

6161
// 🐨 create an async requireUser function
62-
// 🐨 get the user from await this.db.getUserById(Number(this.requireAuthInfo().extra.userId))
62+
// 🐨 get the user from await this.db.getUser()
6363
// 🐨 the user should absolutely exist by this point,
6464
// but just in case (maybe they were deleted from the db?) throw an error if they don't
6565
// 🐨 return the user

exercises/04.user/02.solution.user/src/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,7 @@ You can also help users add tags to their entries and get all tags for an entry.
5959
}
6060

6161
async requireUser() {
62-
const user = await this.db.getUserById(
63-
Number(this.requireAuthInfo().extra.userId),
64-
)
62+
const user = await this.db.getUser()
6563
invariant(user, 'User not found')
6664
return user
6765
}

exercises/04.user/README.mdx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,76 @@
11
# Using the User
2+
3+
Now that we have the auth info, we need to get it into our MCP agent's context so we can make database calls with the user's token and also expose it to our tools and resources. With Cloudflare Workers, this is done through the `ctx.props` mechanism, which allows you to pass data from the request handler into your `McpAgent` instance.
4+
5+
Example:
6+
7+
```ts lines=2-3,8
8+
// In your request handler
9+
const authInfo = await resolveAuthInfo(request.headers.get('authorization'))
10+
ctx.props.authInfo = authInfo
11+
12+
// In your McpAgent
13+
export class EpicMeMCP extends McpAgent<Env, State, Props> {
14+
async init() {
15+
const authInfo = this.props?.authInfo
16+
// ...
17+
}
18+
}
19+
```
20+
21+
This approach ensures that every database operation is authenticated with the user's OAuth token, and every tool and resource can access the current user's information.
22+
23+
<callout-info>
24+
The `ctx.props` mechanism is Cloudflare Workers-specific. It allows you to
25+
pass data from the request handler into your `McpAgent` instance, making
26+
authentication information available throughout your MCP server.
27+
</callout-info>
28+
29+
Here's how the authentication flow works:
30+
31+
```mermaid
32+
sequenceDiagram
33+
participant Client
34+
participant Worker as Cloudflare Worker
35+
participant Auth as Auth Server
36+
participant MCP as EpicMeMCP Agent
37+
participant DB as Database Client
38+
39+
Client->>Worker: MCP Request (with Authorization header)
40+
Worker->>Auth: resolveAuthInfo(authorization)
41+
Auth-->>Worker: Returns AuthInfo (token + user ID)
42+
Worker->>Worker: Set ctx.props.authInfo = authInfo
43+
Worker->>MCP: Create McpAgent with context props
44+
MCP->>MCP: this.requireAuthInfo() gets authInfo from props
45+
MCP->>DB: getClient(authInfo.token) - authenticated client
46+
MCP->>DB: Database operations (user-specific)
47+
DB-->>MCP: Returns user's data only
48+
MCP-->>Worker: MCP Response
49+
Worker-->>Client: Authenticated response
50+
```
51+
52+
The database client you'll be working with accepts an OAuth token as its second parameter, ensuring all database operations are scoped to the authenticated user:
53+
54+
```ts
55+
// Create a client that knows who the user is
56+
const db = getClient(EPIC_ME_AUTH_SERVER_URL, oauthToken)
57+
const entries = await db.getEntries() // Only gets THIS user's entries
58+
```
59+
60+
<callout-warning>
61+
If there's no authentication token, we shouldn't even try to create a database
62+
connection. The user needs to be properly authenticated first!
63+
</callout-warning>
64+
65+
In this exercise, you'll learn how to:
66+
67+
- Pass authentication information through Cloudflare's `ctx.props` system
68+
- Create user-specific database clients that automatically scope data access
69+
- Build utility functions to access user information throughout your MCP server
70+
- Expose user data through MCP tools and resources
71+
- Ensure every operation is properly authenticated and user-scoped
72+
73+
You'll also explore how to structure your `McpAgent` class to handle authentication gracefully, using TypeScript generics to ensure type safety for your context props.
74+
75+
- 📜 [Cloudflare Workers Context](https://developers.cloudflare.com/workers/runtime-apis/context/)
76+
- 📜 [OAuth 2.0 specification](https://tools.ietf.org/html/rfc6749)

exercises/05.scopes/01.problem.check-scopes/src/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ You can also help users add tags to their entries and get all tags for an entry.
6363
}
6464

6565
async requireUser() {
66-
const user = await this.db.getUserById(
67-
Number(this.requireAuthInfo().extra.userId),
68-
)
66+
const user = await this.db.getUser()
6967
invariant(user, 'User not found')
7068
return user
7169
}

exercises/05.scopes/01.solution.check-scopes/src/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,7 @@ You can also help users add tags to their entries and get all tags for an entry.
6161
}
6262

6363
async requireUser() {
64-
const user = await this.db.getUserById(
65-
Number(this.requireAuthInfo().extra.userId),
66-
)
64+
const user = await this.db.getUser()
6765
invariant(user, 'User not found')
6866
return user
6967
}

exercises/05.scopes/02.problem.validate-sufficient-scope/src/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,7 @@ You can also help users add tags to their entries and get all tags for an entry.
6565
}
6666

6767
async requireUser() {
68-
const user = await this.db.getUserById(
69-
Number(this.requireAuthInfo().extra.userId),
70-
)
68+
const user = await this.db.getUser()
7169
invariant(user, 'User not found')
7270
return user
7371
}

0 commit comments

Comments
 (0)